﻿from __future__ import unicode_literals

import types, csv
from collections import OrderedDict
from macro import DEVICE, LadderMacro
import emiter

TIMEOUT_ERROR = 6
SERVO_ERROR = 7
MOTOR_ERROR = 8
INVERTER_ERROR = 9
ORIGIN_ERROR = 10

alarm_desc = {
    TIMEOUT_ERROR: "동작 시간초과",
    SERVO_ERROR: "서보 이상",
    MOTOR_ERROR: "모터 이상",
    INVERTER_ERROR: "인버터 이상",
    ORIGIN_ERROR: "원점 이상",
    }

timerpool = 2000
def alloctimer(count=1):
    global timerpool
    
    try:
        return "T{0:04d}".format(timerpool)
    finally:
        timerpool += count
        if timerpool > 2999:
            raise Exception("타이머가 부족합니다.")
            
class AlarmPool(object):
    def __init__(self, pool=0, max=0):
        self.pool = pool
        self.max = max
        self.alarms = []
        
    def alloc(self, count=1):
        try:
            return "M{0:04d}".format(self.pool)
        finally:
            self.pool += count
            if self.pool >= self.max:
                raise Exception("알람이 부족합니다.")
            
    def log(self, alarm, info):
        self.alarms.append((alarm, info))
        
    def report(self, line, path):
        with open(path, "wb") as f:
            w = csv.writer(f)
            for alarm, (unit, atype) in self.alarms:
                zone_no = 0
                part = line.get_part(unit.zonename)
                if part:
                    zone_no = part.no
                else:
                    import sys
                    print >>sys.stderr, "{0}의 파트를 찾을 수 없습니다.".format(unit.zonename)
                w.writerow([alarm, zone_no, atype, 0, 0, 2, "{0} - {1}".format(
                    alarm_desc[atype], 
                    unit.vars["COMMENT"].replace(".", " ")).encode("utf-8")])
            
_alarm = AlarmPool(8100, 8700)


class FaObject(object):
    def __init__(self, line=None, id=None, name="NONAME"):
        self.line = line
        self.id = id
        self.name = name
        self.vars = {}
        if self.id:
            self.vars["CMD"] = self.id
        
    def _emit(self, code):
        macro = LadderMacro(code, vars=self.vars)
        src = macro.render()
        emiter.emit(self._trans(src))
        
    def _trans(self, src):
        return src
        
            
class FaUnit(FaObject):
    _action_ = ["전진", "후진"]

    def __init__(self, line=None, id=None, zone=None, name="NONAME", factory=None):
        super(FaUnit, self).__init__(line, id, name)
        self.factory = factory
        self.zonename = zone
        self.vars["COMMENT"] = "{0}.{1}".format(self.zonename, self.name)
        self.set_part(self.zonename)
        
        self.vars["EMGC"] = "M1009"
        self.vars["MANUAL"] = "SM400"
        self.vars["MLOCK"] = "SM401"
        self.vars["RESET"] = "M1003"
        self.vars["RESET15"] = "M1004"
        self.vars["PAUSE"] = "SM401"
        
        self.vars["ACTION1"] = self._action_[0]
        self.vars["ACTION2"] = self._action_[1]

    def set_manual(self):
        return self
        
    def set_op(self, name, button1=None, button2=None):
        if name:
            op = self.line.get_opbox(name)
            
            self.vars["MANUAL"] = self.vars["MANUAL1"] = op.get_manual()
            if button1:
                self.vars["BUTTON1"], self.vars["LAMP1"] = op.get_button(button1)
            if button2:
                self.vars["BUTTON2"], self.vars["LAMP2"] = op.get_button(button2)
        
        return self
        
    def set_op2(self, name, button1=None, button2=None):
        if name:
            op = self.line.get_opbox(name)
            
            self.vars["MANUAL2"] = op.get_manual()
            if button1:
                self.vars["BUTTON3"], self.vars["LAMP3"] = op.get_button(button1)
            if button2:
                self.vars["BUTTON4"], self.vars["LAMP4"] = op.get_button(button2)
        
        return self
        
    def get_device(self, index):
        return LadderMacro("{{CMD[{0}]}}".format(index), vars=self.vars).render()
        
    def set_part(self, name):
        part = self.line.get_part(name)
        if not part:
            raise Exception("{0}의 {1} 파트를 찾을 수 없습니다.".format(self.name, name))
        self.vars["PART"] = part.id
        
    def set_end(self):
        return self
        
    def action_count(self):
        return 2
        
    def emit_statement(self):
        self._emit("""\
            ;*{COMMENT}
            """)
            
    def emit_op_button(self):
        """ 
            2개 까지의 OP 버튼을 내부 버튼으로 연결시킨다.
        """
        if self.action_count() == 2 and "BUTTON2" not in self.vars:
            self.emit_op_toggle_button()
        else:
            if "BUTTON1" in self.vars:
                self._emit("""\
            LD {MANUAL1}
            AND {BUTTON1}
        #if BUTTON3
            LD {MANUAL2}
            AND {BUTTON3}
            ORB
        #endif
            OUT {CMD[M8]}
            """)
            
            if "BUTTON2" in self.vars:
                self._emit("""\
            LD {MANUAL1}
            AND {BUTTON2}
        #if BUTTON4
            LD {MANUAL2}
            AND {BUTTON4}
            ORB
        #endif
            OUT {CMD[M9]}
            """)
            
    def emit_op_toggle_button(self):
        if "BUTTON1" in self.vars:
            self._emit("""\
            LD {MANUAL1}
            ANDP {BUTTON1}
        #if BUTTON3
            LD {MANUAL2}
            ANDP {BUTTON3}
            ORB
        #endif
            SET {CMD[M9]}
            AND {CMD[L1]}
            RST {CMD[M9]}
            SET {CMD[M8]}
            
            LDI {BUTTON1}
        #if BUTTON3
            ANI {BUTTON3}
        #endif
            RST {CMD[M8]}
            RST {CMD[M9]}
            """)
            
    def emit_op_lamp(self):
        """ 
            2개 까지의 OP 램프을 내부 램프와 연결시킨다.
        """
        
        if self.action_count() == 2 and "LAMP2" not in self.vars:
            self.emit_op_toggle_lamp()
        else:
            if "LAMP1" in self.vars:
                self._emit("""\
            LD {CMD[L8]}
            OUT {LAMP1}
        #if LAMP3
            OUT {LAMP3}
        #endif
            """)
            
            if "LAMP2" in self.vars:
                self._emit("""\
            LD {CMD[L9]}
            OUT {LAMP2}
        #if LAMP4
            OUT {LAMP4}
        #endif
            """)
        
    def emit_op_toggle_lamp(self):
        if "LAMP1" in self.vars:
            self._emit("""\
            LD {CMD[L0]}
            AND SM411
            AND SM413
            LD {CMD[L1]}
            ORB
            LD {CMD[M0]}
            AND {CMD[L8]}
            ORB
            LD {CMD[M1]}
            AND {CMD[L9]}
            ORB
            OUT {LAMP1}
        #if LAMP3
            OUT {LAMP3}
        #endif
            """)
            
    def emit_button(self):
        self.emit_momentary_button()

    def emit_toggle_button(self):
        self._emit("""\
            LDI {ZONE[L2]}
            MPS
            AND {CMD[M8]}
            FF {CMD[M0]}
            MPP
            AND {CMD[M9]}
            FF {CMD[M1]}
            """)
            
    def emit_momentary_button(self):
        self._emit("""\
            LDI {ZONE[L2]}
            MPS
            ANDP {CMD[M8]}
            SET {CMD[M0]}
            MRD
            ANDF {CMD[M8]}
            RST {CMD[M0]}
            MRD
            ANDP {CMD[M9]}
            SET {CMD[M1]}
            MPP
            ANDF {CMD[M9]}
            RST {CMD[M1]}
            """)
            
    # 수동금지 될 때, 비상정지 때 명령을 지운다.
    # 수동금지 될 때 명령을 지우면,
    # 수동에서 조작된 명령들이 자동 전에 모두 취소되는 효과를 가진다.
    def emit_emergency(self):
        self._emit("""\
            LD {ZONE[L1]}
            ORP {ZONE[L2]}
            RST {CMD[M0]}
            RST {CMD[M1]}
            """)
            
    # 상태가 확인되면 명령을 지운다.
    def emit_complete(self):
        self._emit("""\
            LD {CMD[L0]}
            RST {CMD[M0]}
            LD {CMD[L1]}
            RST {CMD[M1]}
            """)
            
    # 명령간의 인터락
    def emit_command_interlock(self):
        self._emit("""\
            LDP {CMD[M1]}
            RST {CMD[M0]}
            LD {CMD[M0]}
            RST {CMD[M1]}
            """)
        
    # 인터락이 없는 경우 명령에 대한 내부 출력
    def emit_internal_output(self):
        self._emit("""\
            LD {CMD[M0]}
            ANI {CMD[M2]}
            OUT {CMD[L2]}
            LD {CMD[M1]}
            ANI {CMD[M3]}
            OUT {CMD[L3]}
            """)
    
    # 동작 시간 초과
    def emit_timeout(self, timer=2):
        self._emit("""\
            LD {CMD[M0]}
            ANI {CMD[L0]}
            OUT {TIMER[%(T0)d]} R1021
            AND {TIMER[%(T0)d]}
            OUT {CMD[L6]}
            LD {CMD[M1]}
            ANI {CMD[L1]}
            OUT {TIMER[%(T1)d]} R1021
            AND {TIMER[%(T1)d]}
            OUT {CMD[L7]}
            ;;{TIMER[T%(T0)d]}:=시간초과1
            ;;{TIMER[T%(T1)d]}:=시간초과2
            """ % {"T0": timer, "T1": timer + 1})
            
    def emit_timeout_alarm(self):
        self.vars["ALARM"] = alarm = _alarm.alloc(1)
        self._emit("""\
            LD {CMD[L6]}
            OR {CMD[L7]}
            OUT {ALARM}
            ;;{ALARM}:={COMMENT}.시간초과
            """)
        _alarm.log(alarm, (self, TIMEOUT_ERROR))
            
    # 기본 램프
    def emit_lamp(self):
        self._emit("""\
            LD {CMD[L0]}
            LD {CMD[L2]}
            AND SM412
            ORB
            LD {CMD[M0]}
            ANI {CMD[L2]}
            AND SM411
            ORB
            OUT {CMD[L8]}
            LD {CMD[L1]}
            LD {CMD[L3]}
            AND SM412
            ORB
            LD {CMD[M1]}
            ANI {CMD[L3]}
            AND SM411
            ORB
            OUT {CMD[L9]}
            """)
            
    def emit_default_comment(self):
        self._emit("""\
            ;;{CMD[M0]}:={COMMENT}.명령1
            ;;{CMD[M1]}:={COMMENT}.명령2
            ;;{CMD[M2]}:={COMMENT}.금지1
            ;;{CMD[M3]}:={COMMENT}.금지2
            ;;{CMD[M8]}:={COMMENT}.버턴1
            ;;{CMD[M9]}:={COMMENT}.버턴2
            
            ;;{CMD[L0]}:={COMMENT}.상태1
            ;;{CMD[L1]}:={COMMENT}.상태2
            ;;{CMD[L2]}:={COMMENT}.출력1
            ;;{CMD[L3]}:={COMMENT}.출력2
            ;;{CMD[L6]}:={COMMENT}.시간초과1
            ;;{CMD[L7]}:={COMMENT}.시간초과2
            ;;{CMD[L8]}:={COMMENT}.램프1
            ;;{CMD[L9]}:={COMMENT}.램프2
            """)
            
    def emit_action_comment(self):
        self._emit("""\
            ;;{CMD[M0]}:={COMMENT}.{ACTION1}명령
            ;;{CMD[M1]}:={COMMENT}.{ACTION2}명령
            ;;{CMD[M2]}:={COMMENT}.{ACTION1}금지
            ;;{CMD[M3]}:={COMMENT}.{ACTION2}금지
            ;;{CMD[M8]}:={COMMENT}.{ACTION1}버턴
            ;;{CMD[M9]}:={COMMENT}.{ACTION2}버턴
            
            ;;{CMD[L0]}:={COMMENT}.{ACTION1}상태
            ;;{CMD[L1]}:={COMMENT}.{ACTION2}상태
            ;;{CMD[L2]}:={COMMENT}.{ACTION1}출력
            ;;{CMD[L3]}:={COMMENT}.{ACTION2}출력
            ;;{CMD[L6]}:={COMMENT}.{ACTION1}시초
            ;;{CMD[L7]}:={COMMENT}.{ACTION2}시초
            ;;{CMD[L8]}:={COMMENT}.{ACTION1}램프
            ;;{CMD[L9]}:={COMMENT}.{ACTION2}램프
            """)
 
    def emit_emergency_lamp(self):
        self._emit("""\
            LD {PART[L1]}
            CALL P104 {CMD[L8]} {CMD[L9]}
            """)
 
    def emit(self):
        pass

        
class UnitRun(FaObject):
    def __init__(self, line, id, name):
        super(UnitRun, self).__init__(line, id, name)
        #self.vars["TIMER"] = alloctimer(1)
        self.vars["COMMENT"] = name
        
    def emit(self):
        self._emit("""\
            LD {CMD[D8]}.0
            ANI {CMD[M0]}
            SET {CMD[M4]}
            ;;{CMD[M0]}:={COMMENT}.운전준비
            ;;{CMD[M4]}:={COMMENT}.운전준비.요청
            LD {CMD[D8]}.1
            ANI {CMD[M1]}
            SET {CMD[M5]}
            ;;{CMD[M1]}:={COMMENT}.운전
            ;;{CMD[M5]}:={COMMENT}.운전요청
            LD {CMD[D8]}.2
            SET {CMD[M6]}
            ANI {CMD[M2]}
            OUT {CMD[T0]} K30
            AND {CMD[T0]}
            SET {CMD[M7]}
            ;;{CMD[M2]}:={COMMENT}.정지
            ;;{CMD[M6]}:={COMMENT}.정지요청
            ;;{CMD[T0]}:={COMMENT}.강제정지
            ;;{CMD[M7]}:={COMMENT}.강제정지
            
            LD {CMD[L1]}
            SET {CMD[M7]}
            ;;{CMD[L1]}:={COMMENT}.비상정지
            LD {CMD[M7]}
            SET {CMD[M6]}
            LD {CMD[D8]}.3
            FF {CMD[M3]}
            LD {CMD[M3]}
            OUT {CMD[L4]}
            ;;{CMD[M3]}:={COMMENT}.일시정지
            ;;{CMD[D8]}:={COMMENT}.운전버튼
            
            LD {CMD[M5]}
            ANI {CMD[M1]}
            MPS
            ANI {CMD[M0]}
            SET {CMD[M4]}
            MPP
            AND {CMD[M0]}
            SET {CMD[M1]}
            
            LD {CMD[M1]}
            OR {CMD[M6]}
            RST {CMD[M5]}
            LD {CMD[M6]}
            AND {CMD[M2]}
            RST {CMD[M1]}
            
            LD {CMD[M0]}
            OR {CMD[M6]}
            RST {CMD[M4]}
            
            LD {CMD[M1]}
            LD {CMD[M6]}
            AND {CMD[M2]}
            ORB 
            RST {CMD[M0]}
            
            LD {CMD[M2]}
            RST {CMD[M6]}
            RST {CMD[M7]}
            
            LD {CMD[M7]}
            SET {CMD[L1]}
            
            # LAMP
            LD {CMD[M0]}
            LD {CMD[M4]}
            AND SM412
            ORB
            OUT {CMD[D9]}.0
            
            LD {CMD[M1]}
            LD {CMD[M5]}
            AND SM412
            ORB
            OUT {CMD[D9]}.1
            
            LDI {CMD[M1]}
            AND {CMD[M2]}
            LD {CMD[M6]}
            AND SM412
            ORB
            OUT {CMD[D9]}.2
            
            LD {CMD[M3]}
            OUT {CMD[D9]}.3
            ;;{CMD[D9]}:={COMMENT}.운전램프
            """)
            
        self._emit("""\
            LD {CMD[M4]}
            SET {CMD[M0]}
            LD {CMD[M6]}
            SET {CMD[M2]}
            """)
            
def mystyle_device(device):
    if device[0:1] in ["X", "Y", "B", "W"]:
        return device[0] + "$" + device[1:]
    return device
    
def subcondition(L):
    if len(L) < 2:
        return L
    L[0] = "MPS\n" + L[0]
    if len(L) > 2:
        L[1:-1] = ["MRD\n" + i for i in L[1:-1]]
    L[-1] = "MPP\n" + L[-1]
    return L
    
class OpBox(FaObject):
    def __init__(self, line, id, name):
        super(OpBox, self).__init__(line, id=id, name=name)
        self.buttons = {}
        self.id = id
        self.start = 0
        self.shift = None
        self.name = name
        
        self.vars["CMD"] = self.id
        self.vars["COMMENT"] = self.name
        
    def set_button(self, key, button, lamp):
        self.buttons[key] = (button, lamp)
        
        return self
        
    def set_shift(self, shift, offset, keys):
        self.shift  = self.buttons[shift]
        del self.buttons[shift]
        self.start = 10
        self.offset = offset
        self.shift_buttons = keys 
        
        self.vars["SHIFT_BUTTON"] = self.shift[0]
        self.vars["SHIFT_LAMP"] = self.shift[1]
        
        return self
        
    def set_alias(self, alias):
        self.alias = alias
        
        return self
        
    def get_manual(self):
        return self.get_button("1:1")[0]
        
    def _get_addr(self, key):
        r, c = [int(n) for n in key.split(":")]
        return self.start + (r - 1) * 10 + c - 1
        
    def get_button(self, key):
        shift = key[0] == "~"
        if shift:
            key = key[1:]
        addr = int(self.id[1:]) + self._get_addr(key)
        if shift:
            addr += self.offset
        button = "M{0}".format(addr)
        lamp = "L{0}".format(addr)
        return button, lamp
        
    def emit_noshift(self, keys):
        buttons = []
        lamps = []
        comments = []
        for key in keys:
            button, lamp = self.buttons[key]
            addr = self._get_addr(key)
            data = {"button":button, "lamp":lamp, "addr": addr, "key":key}
            buttons.append("""\
                LD {%(button)s}
                OUT {CMD[M%(addr)s]}""" % data)
            comments.append("""\
                ;;{%(button)s}:={COMMENT}.%(key)s.버튼
                ;;{CMD[M%(addr)s]}:={COMMENT}.%(key)s.버튼
                """ % data)
            if lamp:
                lamps.append("""\
                LD {CMD[L%(addr)s]}
                OUT {%(lamp)s}""" % data)
                comments.append("""\
                ;;{%(lamp)s}:={COMMENT}.%(key)s.버튼
                ;;{CMD[L%(addr)s]}:={COMMENT}.%(key)s.램프
                """ % data)
             
                
        buttons = "\n".join(buttons)
        lamps   = "\n".join(lamps)
        comments = "\n".join(comments)
        self._emit(buttons + "\n" + lamps + "\n" + comments)
                
    def emit_shift(self, keys):
        buttons = []
        lamps = []
        comments = []
        for key in keys:
            button, lamp = self.buttons[key]
            addr = self._get_addr(key)
            data = {"button":button, "lamp":lamp, "addr1": addr, "addr2": addr + self.offset, "key": key}
            buttons.append("""\
                LD {%(button)s}
                MPS
                ANI {CMD[M0]}
                OUT {CMD[M%(addr1)s]}
                MPP
                AND {CMD[M0]}
                OUT {CMD[M%(addr2)s]}""" % data)
            comments.append("""\
                ;;{%(button)s}:={COMMENT}.%(key)s.버튼
                ;;{CMD[M%(addr1)s]}:={COMMENT}.%(key)s.버튼1
                ;;{CMD[M%(addr2)s]}:={COMMENT}.%(key)s.버튼2
                """ % data)
            if lamp:
                lamps.append("""\
                LDI {CMD[M0]}
                AND {CMD[L%(addr1)s]}
                LD {CMD[M0]}
                AND {CMD[L%(addr2)s]}
                ORB
                OUT {%(lamp)s}""" % data)
                comments.append("""\
                ;;{%(lamp)s}:={COMMENT}.%(key)s.버튼
                ;;{CMD[L%(addr1)s]}:={COMMENT}.%(key)s.램프1
                ;;{CMD[L%(addr2)s]}:={COMMENT}.%(key)s.램프2
                """ % data)
                
        buttons = "\n".join(buttons)
        lamps   = "\n".join(lamps)
        comments = "\n".join(comments)
        self._emit(buttons + "\n" + lamps + "\n" + comments)
        
    def emit_comment(self):
        src  = ""
        for key, comment in [("1:1", "수동"), ("4:2", "통과"), ("4:3", "공급요구"), ("4:4", "전원"), ("4:5", "작업완료")]:
            button, lamp = self.get_button(key)
            src += ";;%s:=%s\n" % (button, self.name + "." + comment)
            src += ";;%s:=%s\n" % (lamp, self.name + "." + comment + ".램프")
        self._emit(src)
        
    def emit(self):
        self.vars["MANUAL"] = self.get_manual()
        
        if self.shift:
            self._emit("""\
                LD {SHIFT_BUTTON}
                OUT {CMD[M0]}
                OUT {SHIFT_LAMP}
                ;;{CMD[M0]}:={COMMENT}.SHIFT
                """);
            keys = sorted(list(set(self.buttons.keys()) - set(self.shift_buttons)))
            self.emit_noshift(keys)
            keys = sorted(self.shift_buttons)
            self.emit_shift(keys)
            
            self.vars["MANUAL"] = self.get_manual()
        else:
            keys = sorted(self.buttons.keys())
            self.emit_noshift(keys)
            
        self.emit_comment()

class Zone(FaObject):
    def __init__(self, line, id, name):
        super(Zone, self).__init__(line, id, name)
        self.is_blank = True
        
        self.vars["NAME"] = name
        self.vars["CMD"] = id
        
    def set_sensor(self, x1, x2, x3, x4, x5):
        self.is_blank = not (x1 and x2)
        self.vars["진입감지"] = mystyle_device(x1)
        self.vars["도착감지"] = mystyle_device(x2)
        
        return self
        
    def set_pallet_dir(self, x):
        self.vars["방향감지"] = mystyle_device(x)
        
        return self
        
    def set_alias(self, *args):
        self.alias = args[0] if args else self.name
        for alias in args:
            self.line.names[alias] = self.name
        return self
        
    def emit_zone_sensor(self):
        src = """\
            LD  {진입감지}
            OUT {CMD[L0]}
            LD  {도착감지}
            OUT {CMD[L1]}
            ;;{진입감지}:={NAME}.진입감지
            ;;{도착감지}:={NAME}.도착감지
            ;;{CMD[L0]}:={NAME}.진입감지
            ;;{CMD[L1]}:={NAME}.도착감지
            """
        if "방향감지" in self.vars:
            src += """\
            LD  {방향감지}
            OUT {CMD[L2]}
            ;;{방향감지}:={NAME}.방향감지
            ;;{CMD[L2]}:={NAME}.방향감지
            
            LD  {CMD[L0]}
            OR  {CMD[L2]}
            AND {CMD[L1]}
            OUT {CMD[M0]}
            OUT {CMD[T0]} K10
            LDI {CMD[L0]}
            ANI {CMD[L1]}
            ANI {CMD[L2]}
            OUT {CMD[M1]}
            OUT {CMD[T1]} K10
            """
        else:
            src += """\
            LD  {CMD[L0]}
            AND {CMD[L1]}
            OUT {CMD[M0]}
            OUT {CMD[T0]} K10
            LDI {CMD[L0]}
            ANI {CMD[L1]}
            OUT {CMD[M1]}
            OUT {CMD[T1]} K10
            """
        src += """\
            ;;{CMD[M0]}:={NAME}.도착확인
            ;;{CMD[M1]}:={NAME}.배출확인
            ;;{CMD[T0]}:={NAME}.도착지연
            ;;{CMD[T1]}:={NAME}.배출지연
            """
        return src
                
    def emit(self):
        src = ""
        if not self.is_blank:
            src += self.emit_zone_sensor()
        self._emit(src)
        

class Cylinder(FaUnit):
    _action_ = ["전진", "후진"]

    def __init__(self, line, id, zone, name, *args):
        super(Cylinder, self).__init__(line, id, zone, name)
        if args:
            self.set_sensor_sol(*args)
        self.vars["TIMER"] = alloctimer(4)
       
    def set_sensor_sol(self, *args):
        if len(args) == 4:
            x1, x2, y1, y2 = args
            self.vars["SENSOR1A"] = mystyle_device(x1)
            self.vars["SENSOR2A"] = mystyle_device(x2)
        elif len(args) == 6:
            x1, x2, x3, x4, y1, y2 = args
            self.vars["SENSOR1A"] = mystyle_device(x1)
            self.vars["SENSOR1B"] = mystyle_device(x2)
            self.vars["SENSOR2A"] = mystyle_device(x3)
            self.vars["SENSOR2B"] = mystyle_device(x4)
        else:
            raise Exception("invalid sensor count")
        self.vars["SOL1"] = mystyle_device(y1)
        self.vars["SOL2"] = mystyle_device(y2)
        
        return self
        
    def emit_sensor(self):
        if self.vars.get("SENSOR1A"):
            self._emit("""\
            LD {SENSOR1A}
        #if SENSOR1B
            AND {SENSOR1B}
        #endif
            OUT {TIMER[T0]} R1020
            AND {TIMER[T0]}
            OUT {CMD[L0]}
            """)
        elif self.vars.get("SENSOR2A"):
            self._emit("""\
            LDI {SENSOR2A}
        #if SENSOR2B
            ANI {SENSOR2B}
        #endif
            OUT {TIMER[T0]} K10
            AND {TIMER[T0]}
            OUT {CMD[L0]}
            """)
        
        if self.vars.get("SENSOR2A"):
            self._emit("""\
            LD {SENSOR2A}
        #if SENSOR2B
            AND {SENSOR2B}
        #endif
            OUT {TIMER[T1]} R1020
            AND {TIMER[T1]}
            OUT {CMD[L1]}
            """)
        elif self.vars.get("SENSOR1A"):
            self._emit("""\
            LDI {SENSOR1A}
        #if SENSOR1B
            ANI {SENSOR1B}
        #endif
            OUT {TIMER[T1]} K10
            AND {TIMER[T1]}
            OUT {CMD[L1]}
            """)
            
        self._emit("""\
        #if SENSOR1A
            ;;{SENSOR1A}:={COMMENT}.{ACTION1}감지
        #endif
        #if SENSOR1B
            ;;{SENSOR1B}:={COMMENT}.{ACTION1}감지
        #endif
        #if SENSOR2A
            ;;{SENSOR2A}:={COMMENT}.{ACTION2}감지
        #endif
        #if SENSOR2B
            ;;{SENSOR2B}:={COMMENT}.{ACTION2}감지
        #endif
            ;;{CMD[L0]}:={COMMENT}.{ACTION1}확인
            ;;{CMD[L1]}:={COMMENT}.{ACTION2}확인
            ;;{TIMER[T0]}:={COMMENT}.{ACTION1}지연
            ;;{TIMER[T1]}:={COMMENT}.{ACTION2}지연
            """)

        return self
            
    def emit_library(self):
        # 버튼
        self._emit("""\
            LD SM400
            MPS
            ANDP {CMD[M8]}
            SET {CMD[M0]}
            MRD
            ANDF {CMD[M8]}
            RST {CMD[M0]}
            MRD
            ANDP {CMD[M9]}
            SET {CMD[M1]}
            MPP
            ANDF {CMD[M9]}
            RST {CMD[M1]}
            
            LDF {MANUAL}
            OR {EMGC}
            RST {CMD[M0]}
            RST {CMD[M1]}
            ;;{CMD[M0]}:={COMMENT}.{ACTION1}명령
            ;;{CMD[M1]}:={COMMENT}.{ACTION2}명령
            ;;{CMD[M8]}:={COMMENT}.{ACTION1}버튼
            ;;{CMD[M9]}:={COMMENT}.{ACTION2}버튼
            """)
            
        # 인터락
        self._emit("""\
            LDP {CMD[M0]}
            RST {CMD[M1]}
            LD {CMD[M1]}
            RST {CMD[M0]}
            """)
        
        # 출력
        self._emit("""\
            LD {CMD[M0]}
            ANI {CMD[M2]}
            OUT {CMD[L2]}
            LD {CMD[M1]}
            ANI {CMD[M3]}
            OUT {CMD[L3]}
            ;;{CMD[M2]}:={COMMENT}.{ACTION1}금지
            ;;{CMD[M3]}:={COMMENT}.{ACTION2}금지
            """)
            
        # 동작 시간 초과
        self._emit("""\
            LD {CMD[M0]}
            ANI {CMD[L0]}
            OUT {TIMER[T2]} R1021
            AND {TIMER[T2]}
            OUT {CMD[L6]}
            ;;{TIMER[T2]}:={COMMENT}.{ACTION1}시초
            ;;{CMD[L6]}:={COMMENT}.{ACTION1}시초
            LD {CMD[M1]}
            ANI {CMD[L1]}
            OUT {TIMER[T3]} R1021
            AND {TIMER[T3]}
            OUT {CMD[L7]}
            ;;{TIMER[T3]}:={COMMENT}.{ACTION2}시초
            ;;{CMD[L7]}:={COMMENT}.{ACTION2}시초
            """)
    
        # 램프
        self._emit("""\
            LDI {EMGC}
            LD {CMD[L0]}
            LD {CMD[L2]}
            AND SM412
            ORB
            LD {CMD[M0]}
            ANI {CMD[L2]}
            AND SM411
            ORB
            ANB
            LD {EMGC}
            AND SM411
            ORB
            OUT {CMD[L8]}
            ;;{CMD[L8]}:={COMMENT}.{ACTION1}램프
            LDI {EMGC}
            LD {CMD[L1]}
            LD {CMD[L3]}
            AND SM412
            ORB
            LD {CMD[M1]}
            ANI {CMD[L3]}
            AND SM411
            ORB
            ANB
            LD {EMGC}
            AND SM411
            ORB
            OUT {CMD[L9]}
            ;;{CMD[L9]}:={COMMENT}.{ACTION2}램프
            """)
            
        # 명령 완료
        self._emit("""\
            LD {PART[L2]}
            MPS
            AND {CMD[L0]}
            RST {CMD[M0]}
            MPP
            AND {CMD[L1]}
            RST {CMD[M1]}
            """)
            
        return self
        
    def emit_sol(self):
        self._emit("""\
            LD {CMD[L2]}
            OUT {SOL1}
            LD {CMD[L3]}
            OUT {SOL2}
            ;;{CMD[L2]}:={COMMENT}.{ACTION1}출력
            ;;{CMD[L3]}:={COMMENT}.{ACTION2}출력
            ;;{SOL1}:={COMMENT}.{ACTION1}출력
            ;;{SOL2}:={COMMENT}.{ACTION2}출력
            """)
        return self
        
    def emit(self):
        self.emit_statement()
        self.emit_sensor()
        self.emit_op_button()
        self.emit_library()
        self.emit_timeout_alarm()
        self.emit_op_lamp()
        self.emit_sol()
        
    def _trans(self, src):
        return src.replace(".상승", "." + self._action_[0]).replace(".하강", "." + self._action_[1])
        
        
class Stopper(Cylinder):
    _action_ = ["상승", "하강"]
    
    def __init__(self, line, id, zone, name, *args):
        super(Stopper, self).__init__(line, id, zone, name, *args)
        
    def set_sensor_sol(self, x1, x2, y):
        self.vars["UpSensor1"] = mystyle_device(x1)
        self.vars["UpSensor2"] = mystyle_device(x2)
        self.vars["UpSol"] = mystyle_device(y)
        
        return self
        
    def emit_sensor(self):
        self._emit("""\
            LD {UpSensor1}
        #if UpSensor2
            AND {UpSensor2}
        #endif
            OUT {CMD[L0]}
            ;;{UpSensor1}:={COMMENT}.상승감지
        #if UpSensor2
            ;;{UpSensor2}:={COMMENT}.상승감지
        #endif
            ;;{CMD[L0]}:={COMMENT}.상승확인
            
        #if UpSensor2
            LDI {UpSensor1}
            ANI {UpSensor2}
        #else
            LDI {CMD[L0]}
        #endif
            AND {CMD[L3]}
            OUT {TIMER[0]} R1022
            AND {TIMER[0]}
            OUT {CMD[L1]}
            ;;{CMD[L1]}:={COMMENT}.하강확인
            ;;{CMD[L3]}:={COMMENT}.하강출력
            ;;{TIMER[T0]}:={COMMENT}.하강지연
            """)
        
    def set_op(self, name, button1=None, button2=None):
        if not button2:
            button2 = button1
            button1 = None
        super(Stopper, self).set_op(name, button1, button2)
    
        return self
        
    def set_op2(self, name, button1=None, button2=None):
        if not button2:
            button2 = button1
            button1 = None
        super(Stopper, self).set_op2(name, button1, button2)
    
        return self
        
    def action_count(self):
        return 1
        
    def emit_library(self):
        self._emit("""\
            LD {CMD[M9]}
            FF {CMD[M1]}
            LD {PART[L1]}
            RST {CMD[M1]}
            LD {CMD[M1]}
            ANI {CMD[M3]}
            OUT {CMD[L3]}
            ;;{CMD[M1]}:={COMMENT}.하강명령
            ;;{CMD[M3]}:={COMMENT}.하강금지
            ;;{CMD[M9]}:={COMMENT}.하강버튼
            
            LDI {CMD[M1]}
            ANI {CMD[L0]}
            OUT {TIMER[T1]} R1021
            AND {TIMER[T1]}
            OUT {CMD[L6]}
            ;;{TIMER[T1]}:={COMMENT}.상승시초
            ;;{CMD[L6]}:={COMMENT}.상승시초
            LD {CMD[M1]}
            ANI {CMD[L1]}
            OUT {TIMER[T2]} R1021
            AND {TIMER[T2]}
            OUT {CMD[L7]}
            ;;{TIMER[T2]}:={COMMENT}.상승시초
            ;;{CMD[L7]}:={COMMENT}.하강시초
            
            LDI {CMD[L3]}
            AND {CMD[L0]}
            LD {CMD[L3]}
            AND SM412
            ORB
            LD {CMD[M1]}
            ANI {CMD[L3]}
            AND SM411
            ORB
            OUT {CMD[L9]}
            ;;{CMD[L9]}:={COMMENT}.하강램프
            
            LD {PART[L2]}
            ORI {CMD[M9]}
            AND {CMD[L1]}
            RST {CMD[M1]}
            ;;{CMD[M1]}:={COMMENT}.하강명령
            """)

    def emit_sol(self):
        self._emit("""\
            LD {CMD[L3]}
            OUT {UpSol}
            ;;{UpSol}:={COMMENT}.하강출력
            """)
        return self

    def emit(self):
        self.emit_statement()
        self.emit_sensor()
        self.emit_op_button()
        self.emit_library()
        self.emit_timeout_alarm()
        self.emit_op_lamp()
        self.emit_sol()

        
class TurnCylinder(Cylinder):
    _action_ = ["회전", "복귀"]

    
class BackupCylinder(Cylinder):
    _action_ = ["상승", "하강"]
    
 
class ClampCylinder(Cylinder):
    _action_ = ["닫힘", "열림"]
    
    
class Inverter(FaUnit):
    X = DEVICE("X$1000")
    Y = DEVICE("Y$1000")
    R = DEVICE("W$1000")
    W = DEVICE("W$1100")

    def __init__(self, line, id, zone, name, stationNo, selectNo, moduleNo=0, reverse=False, default_speed="K6000"):
        super(Inverter, self).__init__(line, id, zone, name)
        self.stationNo = int(stationNo)
        self.selectNo = int(selectNo)
        self.moduleNo = int(moduleNo)
        self.reverse = reverse
        
        stno = self.stationNo - 1
        xy = self.moduleNo * 0x800 + stno * 32
        rw = self.moduleNo * 0x200 + stno * 4
        self.vars["X"] = self.X[xy]
        self.vars["Y"] = self.Y[xy]
        self.vars["R"] = self.R[rw]
        self.vars["W"] = self.W[rw]
        self.vars["DEFAULT_SPEED"] = default_speed
        
        d = (self.selectNo // 16)
        b = (self.selectNo % 16)
        self.vars["SELECT"]   = "D{0}.{1:X}".format(1176 + d, b)
        self.vars["ENDLAMP"] = "D{0}.{1:X}".format(1178 + d, b)
        
        self.vars["TIMER"] = alloctimer(1)
        
    def emit_library(self):
        # '동작중' 상태값을 얻는다.
        if not self.reverse:
            src = """\
            LD  {X[0]}
            OUT {CMD[L0]}
            LD  {X[1]}
            OUT {CMD[L1]}
            ;;{X[0]}:={COMMENT}.정회전
            ;;{X[1]}:={COMMENT}.역회전
            """
        else:
            src = """\
            LD  {X[1]}
            OUT {CMD[L0]}
            LD  {X[0]}
            OUT {CMD[L1]}
            ;;{X[1]}:={COMMENT}.정회전
            ;;{X[0]}:={COMMENT}.역회전
            """
        src += """\
            ;;{CMD[L0]}:={COMMENT}.정회전
            ;;{CMD[L1]}:={COMMENT}.역회전
            """
        self._emit(src)
            
        # 속도
        self._emit("""\
            LD SM400
            MOV H5 {W[0]}
            OUT {Y[$0C]}
            LD {X[$0C]}
            MOV {R[0]} {CMD[D1]}
            LD<= {CMD[D0]} K0
            MOV {DEFAULT_SPEED} {CMD[D0]}
            LD<> {CMD[D0]} {CMD[D1]}
            MOV {CMD[D0]} {W[1]}
            ANI {X[$0D]}
            OUT {Y[$0D]}
            ;;{R[0]}:={COMMENT}.모니터링.값읽기
            ;;{R[1]}:={COMMENT}.출력속도.읽기
            ;;{W[0]}:={COMMENT}.모니터링.코드쓰기
            ;;{W[1]}:={COMMENT}.설정속도.쓰기
            ;;{X[$0C]}:={COMMENT}.모니터링.완료
            ;;{X[$0D]}:={COMMENT}.속도설정.완료
            ;;{Y[$0C]}:={COMMENT}.모니터링.요청
            ;;{Y[$0D]}:={COMMENT}.속도설정.요청
            ;;{CMD[D0]}:={COMMENT}.속도.설정값
            ;;{CMD[D1]}:={COMMENT}.속도.현재값
            """)
            
        # 파라메터 설정
        self._emit("""\
            LD {SELECT}
            OUT {CMD[L4]}
            LD M1171
            AND {CMD[L4]}
            SET {CMD[M5]}
            LD {X[$0F]}
            OUT {CMD[L5]}
            DMOV {R[2]} {CMD[D4]}
            RST {CMD[M5]}
            
            LD {CMD[M5]}
            OUT {TIMER[T0]} K1
            DMOV D1170 {W[2]}
            ANI {TIMER[T0]}
            DMOV K0 {CMD[D4]}
            LD {TIMER[T0]}
            ANI {CMD[L5]}
            OUT {Y[$0F]}
            LD {CMD[L4]}
            DMOV {CMD[D4]} D1172
            LD {CMD[L5]}
            OUT {ENDLAMP}
            ;;{R[2]}:={COMMENT}.응답코드.읽기
            ;;{R[3]}:={COMMENT}.결과값.읽기
            ;;{W[2]}:={COMMENT}.명령코드.쓰기
            ;;{W[3]}:={COMMENT}.인수값.쓰기
            ;;{X[$0F]}:={COMMENT}.명령실행.완료
            ;;{Y[$0F]}:={COMMENT}.명령실행.요청
            ;;{TIMER[T0]}:={COMMENT}.명령실행.요청지연
            ;;{CMD[D4]}:={COMMENT}.응답코드
            ;;{CMD[D5]}:={COMMENT}.결과값
            ;;{CMD[L4]}:={COMMENT}.명령실행.선택
            ;;{CMD[L5]}:={COMMENT}.명령실행.완료
            ;;{CMD[M5]}:={COMMENT}.명령실행.요청
            """)
            
        # 인터락
        self._emit("""\
            LDP {CMD[M0]}
            RST {CMD[M1]}
            LDP {CMD[M1]}
            RST {CMD[M0]}
            ;;{CMD[M0]}:={COMMENT}.정회전.명령
            ;;{CMD[M1]}:={COMMENT}.역회전.명령
            """)
            
        # 에러
        self.vars["ALARM"] = alarm = _alarm.alloc(1)
        self._emit("""\
            LD {X[X$1A]}
            OUT {CMD[L5]}
            OUT {ALARM}
            LD M1003
            OUT {Y[Y$1A]}
            ;;{X[X$1A]}:={COMMENT}.에러
            ;;{Y[Y$1A]}:={COMMENT}.에러리셋
            """)
        _alarm.log(alarm, (self, INVERTER_ERROR))
        
        # 출력
        src = """\
            LD  {CMD[M0]}
            ANI {CMD[M2]}
            OUT {CMD[L2]}
            LD  {CMD[M1]}
            ANI {CMD[M3]}
            OUT {CMD[L3]}
            ;;{CMD[M2]}:={COMMENT}.정회전.금지
            ;;{CMD[M3]}:={COMMENT}.역회전.금지
            ;;{CMD[L2]}:={COMMENT}.정회전.출력
            ;;{CMD[L3]}:={COMMENT}.역회전.출력
            """
        if not self.reverse:
            src += """\
            LD  {CMD[L2]}
            OUT {Y[0]}
            LD  {CMD[L3]}
            OUT {Y[1]}
            ;;{Y[0]}:={COMMENT}.정회전.출력
            ;;{Y[1]}:={COMMENT}.역회전.출력
            """
        else:
            src += """\
            LD  {CMD[L2]}
            OUT {Y[1]}
            LD  {CMD[L3]}
            OUT {Y[0]}
            ;;{Y[1]}:={COMMENT}.정회전.출력
            ;;{Y[0]}:={COMMENT}.역회전.출력
            """
        self._emit(src)
            
        return self
        
    def emitComment(self):
        # 수동 버튼
        self._emit("""\
            LDI {ZONE[L2]}
            MPS
            AND {CMD[M8]}
            FF  {CMD[M0]}
            MPP
            AND {CMD[M9]}
            FF  {CMD[M1]}
            """)

        # 수동 정위치 정지
        self._emit("""\
            LDI {ZONE[L2]}
            MPS
            AND {CMD[M8]}
            RST {CMD[M0]}
            MPP
            AND {CMD[M9]}
            RST {CMD[M1]}
            """)
            
        return self
        
    def emit(self):
        self.emit_statement()
        self.emit_library()
        
        return self
        
        
class Conveyer(FaUnit):
    def __init__(self, line, id, zone, name):
        super(Conveyer, self).__init__(line, id, zone, name)
        
        # 박스 컨베이어에 대해 브레이크가 MC에 연결된 것을 반영한다.
        self.box = False
        
    def set_mc(self, mc, trip):
        self.ismc = True
        self.vars["MC"] = mc
        self.vars["TRIP"] = trip
        
        return self
        
    def set_inv(self, inv=None, ccw=False):
        self.ismc = False
        self.ccw = ccw
        if not inv:
            inv = self.line.get_unit(self.zonename + ".C/V INV")
        self.vars["INV"] = inv.id
       
        return self
        
    def set_mccb(self, mccb, box=False):
        self.box = box
        self.vars["MCCB"] = mccb
        
        return self
        
    def action_count(self):
        return 1
    
    def emit(self):
        self.emit_statement()
        self.emit_op_button()
        
        # 버튼
        self._emit("""\
            LD {MANUAL}
            ANDP {CMD[M8]}
            FF {CMD[M0]}
            
            ;;{CMD[M0]}:={COMMENT}.정회전.명령
            ;;{CMD[M6]}:={COMMENT}.수동금지
            ;;{CMD[M8]}:={COMMENT}.정회전.버튼
            """)
            
        # 명령
        self._emit("""\
            LD {EMGC}
            RST {CMD[M0]}
            
            LD {CMD[M0]}
            ANI {CMD[M2]}
            OUT {CMD[L2]}
            """)
            
        # 램프
        self._emit("""\
            LD {CMD[L2]}
            LD {CMD[L0]}
            AND SM411
            ORB
            LD {CMD[M2]}
            AND SM412
            ORB
            LD {CMD[L4]}
            AND SM413
            ORB
            OUT {CMD[L8]}
            
            LD {EMGC}
            AND SM411
            ORB
        
            ;;{CMD[L0]}:={COMMENT}.TRIP
            ;;{CMD[L2]}:={COMMENT}.정회전.출력
            ;;{CMD[L4]}:={COMMENT}.절전
            ;;{CMD[M2]}:={COMMENT}.회전금지
            """)
            
        self.emit_op_lamp()
        
        if self.ismc:
            self._emit("""\
                LD {CMD[L2]}
                OUT {MC}
                """)
        else:
            self._emit("""\
                LD {CMD[L2]}
                OUT {INV[M0]}
                """)
                
class BoxConveyer(FaUnit):
    _action_ = ["정회전.", "역회전."]
    
    def __init__(self, line, id, zone, name, unittype, inv=None, power=None):
        super(BoxConveyer, self).__init__(line, id, zone, name)
        self.vars["TIMER"] = alloctimer(3)
        self.vars["COMMENT"] = self.keyname
        if inv == None:
            inv = self.line.get_unit(self.zonename + ".C/V INV")
        self.vars["INV"] = inv.id
        self.power = power
        if self.power:
            self.vars["POWER"] = self.power
           
    @property
    def keyname(self):
        return "{0}.{1}".format(self.zonename, self.name)
        
    def emit_state(self):
        self._emit("""\
            LD {INV[L0]}
            OUT {CMD[L0]}
            LD {INV[L1]}
            OUT {CMD[L1]}
            """)
            
    def XXXX_emit_output(self):
        return """\
            LD {CMD[L2]}
            OR {CMD[L3]}
            OR {CMD[L0]}
            OR {CMD[L1]}
            OR M1172
            OUT {POWER}
            OUT {TIMER[T2]} K15
            ;;{POWER}:={COMMENT}.전원
            
            LD {TIMER[T2]}
            MPS
            AND {CMD[L2]}
            OUT {INV[M0]}
            MPP
            AND {CMD[L3]}
            OUT {INV[M1]}
            """
            
    def emit_output(self):
        self._emit("""\
            LD SM400
            OUT {POWER}
            OUT {TIMER[T2]} K15
            ;;{POWER}:={COMMENT}.전원
            
            LD {TIMER[T2]}
            MPS
            AND {CMD[L2]}
            OUT {INV[M0]}
            MPP
            AND {CMD[L3]}
            OUT {INV[M1]}
            """)
        
    def emit(self):
        self.emit_statement()
        self.emit_state()
        self.emit_toggle_button()
        self.emit_emergency()
        self.emit_command_interlock()
        self.emit_internal_output()
        self.emit_timeout(0)
        self.emit_lamp()
        self.emit_output()
        self.emit_action_comment()
        
        
class PowerMoller(FaUnit):
    def __init__(self, line, id, zone, name):
        super(PowerMoller, self).__init__(line, id, zone, name)
        self.vars["CMD"] = self.id
    
        self.vars["TIMER"] = alloctimer(2)
        
    def set_io(self, err, run1, run2=None):
        self.vars["RUN1"] = run1
        if run2:
            self.vars["RUN2"] = run2
        self.vars["ERR"] = err
        
        return self
        
    def action_count(self):
        if "RUN2" in self.vars:
            return 2
        return 1
        
    def emit(self):
        self.emit_statement()
        
        self.emit_op_button()
        
        # 버튼
        self._emit("""\
            LD {CMD[M8]}
            FF {CMD[M0]}
        #if RUN2
            LD {CMD[M9]}
            FF {CMD[M1]}
        #endif
            
            ;;{CMD[M0]}:={COMMENT}.정회전.명령
            ;;{CMD[M1]}:={COMMENT}.역회전.명령
            ;;{CMD[M6]}:={COMMENT}.수동금지
            ;;{CMD[M8]}:={COMMENT}.정회전.버튼
            ;;{CMD[M9]}:={COMMENT}.역회전.버튼
            """)
            
        # 에러
        self._emit("""\
            LD {ERR}
            OUT {CMD[L0]}
            
            LD {CMD[M0]}
            OUT {TIMER[T0]} K300
            AND {TIMER[T0]}
            OUT {CMD[L6]}
        #if RUN2
            LD {CMD[M1]}
            OUT {TIMER[T1]} K300
            AND {TIMER[T1]}
            OUT {CMD[L7]}
        #endif
            
            ;;{CMD[L0]}:={COMMENT}.컨트롤러.에러
            ;;{CMD[L6]}:={COMMENT}.정회전.시간초과
            ;;{CMD[L7]}:={COMMENT}.역회전.시간초과
            """)
            
        # 명령
        self._emit("""\
            LD {EMGC}
            RST {CMD[M0]}
        #if RUN2
            RST {CMD[M1]}
        #endif
        
        #if RUN2
            LDP {CMD[M0]}
            RST {CMD[M1]}
            LD {CMD[M1]}
            RST {CMD[M0]}
        #endif
            
            LDI {CMD[L6]}
            ANI {CMD[L7]}
        #if RUN2
            MPS
        #endif
            AND {CMD[M0]}
            ANI {CMD[M2]}
            OUT {CMD[L2]}
        #if RUN2
            MPP
            AND {CMD[M1]}
            ANI {CMD[M3]}
            OUT {CMD[L3]}
        #endif
            """)
            
        self._emit("""\
        #if RUN2
        #endif
            """)
            
        # 램프
        self._emit("""\
            LDI {EMGC}
            LDI {CMD[L0]}
            LD {CMD[L2]}
            LD {CMD[M0]}
            ANI {CMD[L2]}
            AND SM411
            AND SM413
            ORB
            ANB
            LD {CMD[L0]}
            AND SM411
            ORB
            ANB
            LD {EMGC}
            AND SM411
            ORB
            OUT {CMD[L8]}
            
        #if RUN2
            LDI {EMGC}
            LDI {CMD[L0]}
            LD {CMD[L3]}
            LD {CMD[M1]}
            ANI {CMD[L3]}
            AND SM411
            AND SM413
            ORB
            ANB
            LD {CMD[L0]}
            AND SM411
            ORB
            ANB
            LD {EMGC}
            AND SM411
            ORB
            OUT {CMD[L9]}
        #endif
            
            ;;{CMD[L0]}:={COMMENT}.TRIP
            ;;{CMD[L2]}:={COMMENT}.정회전.출력
            ;;{CMD[L3]}:={COMMENT}.역회전.출력
            ;;{CMD[M2]}:={COMMENT}.정회전금지
            ;;{CMD[M3]}:={COMMENT}.역회전금지
            """)
            
        self.emit_op_lamp()
        
        self._emit("""\
            LD {CMD[L2]}
        #if RUN2
            OR {CMD[L3]}
        #endif
            OUT {RUN1}
        #if RUN2
            LD {CMD[L3]}
            OUT {RUN2}
        #endif
            """)
                
class Shuttle(FaUnit):
    def __init__(self, line, id, zone, name):
        super(Shuttle, self).__init__(line, id, zone, name)
        self.zone = line.get_zone(zone)
        self.part = self.set_part(zone)
        self.name = name
        
        self.vars["CMD"] = mystyle_device(self.id)
        
        self.vars["TIMER"] = alloctimer(4)
        
    def get_inverter(self):
        name = self.zone.name + ".L/F INV"
        if name in self.line.units:
            return self.line.units[name]
        raise KeyError(name)
        
    def set_dio_inv(self, *args):
        self.vars["BRAKE"] = args[0]
        self.vars["FORRUN"] = args[1]
        self.vars["REVRUN"] = args[2]
        self.vars["FOROUT"] = args[1]
        self.vars["REVOUT"] = args[2]
        #self.vars["LOWSPEED"] = args[3]
        
        return self
        
    def set_comm_inv(self):
        inv = self.get_inverter()
        self.vars["INV"] = inv.id
        #self.vars["BRAKE"] = "M4"
        self.vars["FORRUN"] = inv.get_device("L0")
        self.vars["REVRUN"] = inv.get_device("L1")
        self.vars["FOROUT"] = inv.get_device("M0")
        self.vars["REVOUT"] = inv.get_device("M1")
        self.vars["SPEED"] = inv.get_device("D0")
        
        return self
        
    def set_sensor(self, *args):
        self.vars["DEC1"] = args[0]
        self.vars["POS1"] = args[1]
        self.vars["DEC2"] = args[2]
        self.vars["POS2"] = args[3]
        self.vars["LIMIT1"] = args[4]
        self.vars["LIMIT2"] = args[5]
        
        return self
        
    def emit_position(self):
        # 위치를 확인한다.
        self._emit("""\
            LD SM400
            MOV K0 {CMD[D1]}
            
        #if DEC1
            LD {DEC1}
            MOV K17 {CMD[D1]}
            ;;{DEC1}:={COMMENT}.{MOTION1}감속
        #endif
        #if POS1
            LD {POS1}
            MOV K20 {CMD[D1]}
            OUT {CMD[L0]}
            OUT {CMD[D3]}.1
            ;;{POS1}:={COMMENT}.{MOTION1}위치
        #endif
        #if DEC2
            LD {DEC2}
            MOV K13 {CMD[D1]}
            ;;{DEC2}:={COMMENT}.{MOTION2}감속
        #endif
        #if POS2
            LD {POS2}
            MOV K10 {CMD[D1]}
            OUT {CMD[L1]}
            OUT {CMD[D3]}.3
            ;;{POS2}:={COMMENT}.{MOTION2}위치
        #endif
            LDI {LIMIT1}
            MOV K23 {CMD[D1]}
            OUT {CMD[D3]}.4
            LDI {LIMIT2}
            MOV K7 {CMD[D1]}
            OUT {CMD[D3]}.5
            
            ;;{LIMIT1}:={COMMENT}.{MOTION1}한계
            ;;{LIMIT2}:={COMMENT}.{MOTION2}한계
            ;;{CMD[L0]}:={COMMENT}.{MOTION1}위치
            ;;{CMD[L1]}:={COMMENT}.{MOTION2}위치
            
            LD<> {CMD[D1]} K0
            MOV {CMD[D1]} {CMD[D0]}
            LD= {CMD[D1]} K0
            AND<> {CMD[D2]} K0
            MPS
            AND {FORRUN}
            + K1 {CMD[D0]}
            MPP
            AND {REVRUN}
            - K1 {CMD[D0]}
            
            LD SM400
            MOV {CMD[D1]} {CMD[D2]}
            
            ;;{CMD[D0]}:={COMMENT}.현재위치
            ;;{CMD[D1]}:={COMMENT}.감지위치
            ;;{CMD[D2]}:={COMMENT}.이전위치
            ;;{CMD[D3]}:={COMMENT}.위치센서
            """)
            
    def emit_simple_position(self):
        # 위치를 확인한다.
        self._emit("""\
            LD SM400
            MOV K0 {CMD[D1]}
            
        #if DEC1
            LD {DEC1}
            MOV K17 {CMD[D1]}
            ;;{DEC1}:={COMMENT}.{MOTION1}감속
        #endif
            LD {POS1}
            MOV K20 {CMD[D1]}
            OUT {CMD[L0]}
            OUT {CMD[D3]}.1
        #if DEC2
            LD {DEC2}
            MOV K13 {CMD[D1]}
            ;;{DEC2}:={COMMENT}.{MOTION2}감속
        #endif
            LD {POS2}
            MOV K10 {CMD[D1]}
            OUT {CMD[L1]}
            OUT {CMD[D3]}.3
            LDI {LIMIT1}
            MOV K23 {CMD[D1]}
            OUT {CMD[D3]}.4
            LDI {LIMIT2}
            MOV K7 {CMD[D1]}
            OUT {CMD[D3]}.5
            
            ;;{POS1}:={COMMENT}.{MOTION1}위치
            ;;{POS2}:={COMMENT}.{MOTION2}위치
            ;;{LIMIT1}:={COMMENT}.{MOTION1}한계
            ;;{LIMIT2}:={COMMENT}.{MOTION2}한계
            ;;{CMD[L0]}:={COMMENT}.{MOTION1}위치
            ;;{CMD[L1]}:={COMMENT}.{MOTION2}위치
            
            LD<> {CMD[D1]} K0
            MOV {CMD[D1]} {CMD[D0]}
            LD= {CMD[D1]} K0
            AND<> {CMD[D2]} K0
            MPS
            AND {FORRUN}
            + K1 {CMD[D0]}
            MPP
            AND {REVRUN}
            - K1 {CMD[D0]}
            
            LD SM400
            MOV {CMD[D1]} {CMD[D2]}
            
            ;;{CMD[D0]}:={COMMENT}.현재위치
            ;;{CMD[D1]}:={COMMENT}.감지위치
            ;;{CMD[D2]}:={COMMENT}.이전위치
            ;;{CMD[D3]}:={COMMENT}.위치센서
            """)
    
    # 명령
    def emit_command(self):
        # 명령 인터락
        self._emit("""\
            LDP {CMD[M0]}
            RST {CMD[M1]}
            LDP {CMD[M1]}
            RST {CMD[M0]}
            """)
        
        # 위치 다시잡기
        self._emit("""\
            LDI {CMD[L0]}
            ANI {CMD[L1]}
            RST {CMD[M6]}
            RST {CMD[M7]}
            
            LD {CMD[L0]}
            ANDP {CMD[M0]}
            LD {CMD[L1]}
            ANDP {CMD[M1]}
            ORB
            ANI {CMD[M6]}
            SET {CMD[M7]}
            
            LD {CMD[M0]}
            AND {CMD[L0]}
            LD {CMD[M1]}
            AND {CMD[L1]}
            ORB
            ANI {CMD[M7]}
            SET {CMD[M6]}
            
            LDI {CMD[M6]}
            RST {CMD[L0]}
            RST {CMD[L1]}
            
            LD {CMD[L0]}
            RST {CMD[M0]}
            LD {CMD[L1]}
            RST {CMD[M1]}
            
            ;;{CMD[M6]}:={COMMENT}.정상위치
            ;;{CMD[M7]}:={COMMENT}.재위치.요청
            """)

        # 출력
        self._emit("""\
            LD {CMD[D3]}.6
            AND {CMD[D3]}.7
            OR SM400
            MPS
            ANI {CMD[D3]}.4
            OUT {CMD[L4]}
            MPP
            ANI {CMD[D3]}.5
            OUT {CMD[L5]}
            
            ;;{CMD[M2]}:={COMMENT}.{MOTION1}금지
            ;;{CMD[M3]}:={COMMENT}.{MOTION2}금지
            ;;{CMD[L4]}:={COMMENT}.{MOTION1}안전
            ;;{CMD[L5]}:={COMMENT}.{MOTION2}안전

            LDI {CMD[M7]}
            AND {CMD[M0]}
            LD {CMD[M7]}
            AND {CMD[M1]}
            ORB
            ANI {CMD[M2]}
            AND {CMD[L4]}
            OUT {CMD[L2]}
            
            LDI {CMD[M7]}
            AND {CMD[M1]}
            LD {CMD[M7]}
            AND {CMD[M0]}
            ORB
            ANI {CMD[M3]}
            AND {CMD[L5]}
            OUT {CMD[L3]}
            
            ;;{CMD[L2]}:={COMMENT}.{MOTION1}출력
            ;;{CMD[L3]}:={COMMENT}.{MOTION2}출력
            """)
            
        # 속도
        self._emit("""\
            LD< {CMD[D0]} K10
            OR> {CMD[D0]} K20
            LD {FORRUN}
            AND>= {CMD[D0]} K17
            ORB
            LD {REVRUN}
            AND<= {CMD[D0]} K13
            ORB
            OR {CMD[M7]}
            SET {CMD[M4]}
            ;;{CMD[M4]}:={COMMENT}.저속명령
            
            LD {CMD[L0]}
            OR {CMD[L1]}
            ANI {CMD[M7]}
            RST {CMD[M4]}
            """)
            
        if "INV" in self.vars:
            self._emit("""\
            LDI {CMD[M4]}
            MOV {CMD[R0]} {SPEED}
            LD {CMD[M4]}
            MOV {CMD[R1]} {SPEED}
            ;;{CMD[R0]}:={COMMENT}.고속속도
            ;;{CMD[R1]}:={COMMENT}.저속속도
            """)
        else:
            pass
        
        # 동작 시간 초과
        self._emit("""\
            LD {CMD[M0]}
            ANI {CMD[L0]}
            OUT {TIMER[T0]} R1021
            AND {TIMER[T0]}
            OUT {CMD[L6]}
            LD {CMD[M1]}
            ANI {CMD[L1]}
            OUT {TIMER[T1]} R1021
            AND {TIMER[T1]}
            OUT {CMD[L7]}
            
            ;;{TIMER[T0]}:={COMMENT}.{MOTION1}시초
            ;;{TIMER[T1]}:={COMMENT}.{MOTION2}시초
            ;;{CMD[L6]}:={COMMENT}.{MOTION1}시초
            ;;{CMD[L7]}:={COMMENT}.{MOTION2}시초
            """)
            
        # 램프
        self._emit("""\
            LD {CMD[D3]}.1
            AND {CMD[M6]}
            LD {CMD[D3]}.1
            ANI {CMD[M6]}
            AND SM411
            AND SM413
            ORB
            LD {CMD[L2]}
            AND SM412
            ORB
            LD {CMD[M0]}
            ANI {CMD[L2]}
            AND SM411
            ORB
            OUT {CMD[L8]}
            
            LD {CMD[D3]}.3
            AND {CMD[M6]}
            LD {CMD[D3]}.3
            ANI {CMD[M6]}
            AND SM411
            AND SM413
            ORB
            LD {CMD[L3]}
            AND SM412
            ORB
            LD {CMD[M1]}
            ANI {CMD[L3]}
            AND SM411
            ORB
            OUT {CMD[L9]}
            
            LD {PART[L1]}
            CALL P104 {CMD[L8]} {CMD[L9]}
            
            ;;{CMD[L8]}:={COMMENT}.{MOTION1}램프
            ;;{CMD[L9]}:={COMMENT}.{MOTION2}램프
            """)
            
            
            
        # 출력
        # 회전 출력을 밑에 두고, 브레이크 출력을 위에 둠으로써
        # 회전 출력이 나가고 한 스캔 후에 브레이크가 풀리게 된다.
        # 반대로 내부 출력이 끊어지면서 브레이크는 잠기더라도 
        # 회전 출력은 한 스캔을 더 나가도록 했다.
        src = """\
        #if BRAKE
            LD {CMD[L2]}
            AND {FOROUT}
            LD {CMD[L3]}
            AND {REVOUT}
            ORB
            OUT {BRAKE}
            ;;{BRAKE}:={COMMENT}.BRAKE
        #endif
        #if FOROUT
            LD {CMD[L2]}
            ORF {CMD[L2]}
            OUT {FOROUT}
        #endif
        #if REVOUT
            LD {CMD[L3]}
            ORF {CMD[L3]}
            OUT {REVOUT}
        #endif
            """
            
        # 인버터 타입이 아니면 출력에 주석을 넣는다.
        # 인버터 타입은 인버터 처리 과정에서 이미 주석이 들어가 있다.
        if "INV" not in self.vars:
            src += """\
        #if FOROUT
            ;;{FOROUT}:={COMMENT}.정회전.출력
        #endif
        #if REVOUT
            ;;{REVOUT}:={COMMENT}.역회전.출력
        #endif
            """
        self._emit(src)
            
            
        return

            
        # 수동 정위치 정지
        self._emit("""\
            LDI {ZONE[L2]}
            MPS
            LDP {CMD[M8]}
            FF {CMD[M0]}
            MPP
            AND {CMD[M9]}
            RST {CMD[M1]}
            """)
            
    def emit(self):
        self.emit_statement()
        self.emit_position()
        self.emit_op_button()
        self.emit_button()
        self.emit_command()
        self.emit_op_lamp()

    
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

class PositionData(object):
    def __init__(self, pno=0, pname=None, offset=-1, relative=0, speed=100, indexing=False):
        if not pname:
            pname = "P{0:03d}".format(pno)
        self.no = pno
        self.name = pname
        self.offset = offset
        self.relative = relative
        self.speed = speed
        self.indexing = indexing
        

class Servo(FaUnit):
    def __init__(self, line, id, zone, name, module, axis, mccb, mc, brake):
        super(Servo, self).__init__(line, id, zone, name)
        self.axis = int(axis) - 1
        self.module = int(module, 16) & 0xfff0
        
        self.vars["MODULE"] = "U${0:X}\\".format(self.module / 16)
        self.vars["U"] = "U${0:X}".format(self.module / 16)
        self.vars["AXIS"] = "{0}".format(self.axis + 1)
        self.vars["MX"]  = "X${0:X}".format(self.module + self.axis)
        self.vars["MY"]  = "Y${0:X}".format(self.module + self.axis)
        self.vars["MX2"] = "X${0:X}".format(self.module + self.axis * 2)
        self.vars["MY2"] = "Y${0:X}".format(self.module + self.axis * 2)
        self.vars["CTRLBUF"] = "G{0}".format(self.axis * 100)
        self.vars["PARABUF"] = "G{0}".format(self.axis * 150)
        self.vars["DATABUF"] = "G{0}".format(self.axis * 6000)
        self.vars["CMD"] = self.id
        self.vars["TIMER"] = alloctimer(10)
        self.vars["COMMENT"] = "{0}.{1}".format(zone, name)
        self.vars["MCCB"] = mccb
        self.vars["POWER_MC"] = mc
        if brake:
            self.vars["BRAKE"] = brake
        
        # Z2: ModuleI/OIndex
        # Z3: ModuleIndex
        
        # Z4: ModuleI/O+Axis
        # Z5: ModuleI/O+Axis*2
        
        # Z6: CtrlBufIndex
        # Z7: ParaBufIndex
        # Z8: DataBufIndex
       
        self.sync = False
        
        self.plist = []
        
    def set_sync(self, sync):
        self.sync = True
        self.vars["SYNC"] = sync
        
        return self
        
    def add_pos(self, *args, **kwargs):
        self.plist.append(PositionData(*args, **kwargs))
        
        return self
        
    def emit_buffer(self):
        # 상태
        self._emit("""\
            LD SM400
            DMOV {MODULE}{CTRLBUF[G800]} {CMD[D2]}
            D- {CMD[D2]} {CMD[R14]} {CMD[D0]}
            D+ {CMD[R12]} {CMD[D0]}
            DMOV {MODULE}{CTRLBUF[G804]} {CMD[D4]}
            DMOV {MODULE}{PARABUF[G10]} {CMD[D6]} 
            
            DMOV {MODULE}{CTRLBUF[G806]} {CMD[D10]}
            DMOV {MODULE}{CTRLBUF[G816]} {CMD[D12]}
            MOV  {MODULE}{CTRLBUF[G809]} {CMD[D14]}
            MOV  {MODULE}{CTRLBUF[G877]} {CMD[D15]}
            MOV  {MODULE}{CTRLBUF[G878]} {CMD[D16]}
            MOV  {MODULE}{CTRLBUF[G879]} {CMD[D17]}
            MOV  {MODULE}{CTRLBUF[G880]} {CMD[D18]}
            
            ;;{CMD[D0]}:={COMMENT}.현재위치
            ;;{CMD[D2]}:={COMMENT}.서보위치
            ;;{CMD[D4]}:={COMMENT}.현재속도
            ;;{CMD[D6]}:={COMMENT}.최대속도
            ;;{CMD[D10]}:={COMMENT}.에러
            ;;{CMD[D11]}:={COMMENT}.경고
            ;;{CMD[D12]}:={COMMENT}.외부신호
            ;;{CMD[D13]}:={COMMENT}.상태
            ;;{CMD[D14]}:={COMMENT}.축상태
            ;;{CMD[D15]}:={COMMENT}.서보상태
            ;;{CMD[D16]}:={COMMENT}.회생부하
            ;;{CMD[D17]}:={COMMENT}.부하율
            ;;{CMD[D18]}:={COMMENT}.최대부하
            
            LD {CMD[M0]}
            OUT {TIMER[T0]} K20
            AND {TIMER[T0]}
            OUT {CMD[M0]}
            
            LD {CMD[M0]}
            AND {CMD[L7]}
            AND {CMD[L8]}
            AND {CMD[M12]}
            OUT {CMD[L0]}
            
            LD {MX[X$C]}
            OUT {CMD[L1]}
            LD {MX[X$8]}
            OUT {CMD[L2]}
            LDI {CMD[D13]}.3
            OUT {CMD[L3]}
            LD {CMD[D12]}.0
            OUT {CMD[L4]}
            LD {CMD[D12]}.6
            OUT {CMD[L5]}
            LD {CMD[D12]}.1
            OUT {CMD[L6]}
            LD {CMD[D15]}.0
            OUT {CMD[L7]}
            LD {CMD[M0]}
            AND {CMD[D15]}.1
            OUT {TIMER[T1]} K5
            AND {TIMER[T1]}
            OUT {CMD[L8]}
            LD {CMD[D15]}.F
            OUT {CMD[L9]}
            
            ;;{CMD[M0]}:={COMMENT}.PowerOn
            ;;{CMD[M2]}:={COMMENT}.BrakeOff
            
            ;;{CMD[L0]}:={COMMENT}.준비됨
            ;;{CMD[L1]}:={COMMENT}.동작중
            ;;{CMD[L2]}:={COMMENT}.이상
            ;;{CMD[L3]}:={COMMENT}.원점
            ;;{CMD[L4]}:={COMMENT}.RLS
            ;;{CMD[L5]}:={COMMENT}.DOG
            ;;{CMD[L6]}:={COMMENT}.FLS
            ;;{CMD[L7]}:={COMMENT}.ReadyOn
            ;;{CMD[L8]}:={COMMENT}.ServoOn
            ;;{CMD[L9]}:={COMMENT}.경고
            
            ;;{TIMER[T0]}:={COMMENT}.PowerOn.지연
            ;;{TIMER[T1]}:={COMMENT}.ServoOn.지연
            """)
            
        self.vars["ALARM"] = alarm = _alarm.alloc(1)
        self._emit("""\
            LD {CMD[L2]}
            LD {CMD[L9]}
            AND SM401
            ORB
            OUT {ALARM}
            ;;{ALARM}:={COMMENT}.서보이상
            """)
        _alarm.log(alarm, (self, SERVO_ERROR))
            
        # 가/감속
        self._emit("""\
            LD<= {CMD[R2]} K0
            DMOV K1000 {CMD[R2]}
            LD<= {CMD[R4]} K0
            DMOV K1000 {CMD[R4]}
            LD SM400
            DMOV {CMD[R2]} {MODULE}{PARABUF[G12]}
            DMOV {CMD[R4]} {MODULE}{PARABUF[G14]}
            
            ;;{CMD[R2]}:={COMMENT}.가속시간
            ;;{CMD[R4]}:={COMMENT}.감속시간
            """)
            
        # 속도
        self._emit("""\
            LD {CMD[M22]}
            DMOV R1000 {CMD[R8]}
            LD {CMD[M23]}
            DMOV R1002 {CMD[R8]}
            LD {CMD[M24]}
            DMOV R1004 {CMD[R8]}
            
            LDD< {CMD[R8]} K0
            DMOV K1 {CMD[R8]}
            LDD> {CMD[R8]} K1000
            DMOV K1000 {CMD[R8]}
            
            LDD< {CMD[R8]} R1002
            OUT {CMD[L22]}
            LDD> {CMD[R8]} R1000
            ANDD< {CMD[R8]} R1004
            OUT {CMD[L23]}
            LDD> {CMD[R8]} R1002
            OUT {CMD[L24]}
            
            ;;{CMD[M22]}:={COMMENT}.저속버튼
            ;;{CMD[M23]}:={COMMENT}.중속버튼
            ;;{CMD[M24]}:={COMMENT}.고속버튼
            
            ;;{CMD[L22]}:={COMMENT}.저속램프
            ;;{CMD[L23]}:={COMMENT}.중속램프
            ;;{CMD[L24]}:={COMMENT}.고속램프
            """)
            
        # 리셋
        self._emit("""\
            LD M1003
            MOV K1 {MODULE}{CTRLBUF[G1502]}
            """)
            
        # 정지요청
        self._emit("""\
            LD {CMD[M28]}
            OR M1040
            OR {PART[L1]}
            OR {PART[L4]}
            SET {CMD[M9]}
            
            ;;{CMD[M28]}:={COMMENT}.정지버튼
            ;;{CMD[M9]}:={COMMENT}.정지요청
            """)
            
        # 원점
        self._emit("""\
            LDI {CMD[D99]}.1
            ANI {CMD[D99]}.2
            AND {CMD[M26]}
            ANI {CMD[M81]}
            MOV K9001 {CMD[D31]}
            
            LD {CMD[M25]}
            OUT {TIMER[T2]} K15
            AND {TIMER[T2]}
            DMOV {CMD[D2]} {CMD[R14]}
            OUT {CMD[L25]}
            
            ;;{CMD[M25]}:={COMMENT}.원점지정.버튼
            ;;{CMD[M26]}:={COMMENT}.원점버튼
            ;;{CMD[L25]}:={COMMENT}.원점지정.램프
            ;;{CMD[D31]}:={COMMENT}.명령.위치번호
            
            ;;{TIMER[T2]}:={COMMENT}.원점지정.버튼
            """)
            
        # 이동
        self._emit("""\
            LDI M1040
            AND {CMD[M27]}
            ANI {CMD[M81]}
            MOV K100 {CMD[D31]}
            
            ;;{CMD[M27]}:={COMMENT}.명령버튼
            """)
            
        # 조그
        self._emit("""\
            LDD<> {CMD[D36]} {CMD[R8]}
            DMOV {CMD[R8]} {CMD[D36]}
            MPS
            ANDD> {CMD[D36]} K250
            DMOV K250 {CMD[D36]}
            MPP
            D*  {CMD[D6]} {CMD[D36]} D130
            D/ D130 K2000 D130
            DMOV D130 {CMD[D44]}
            D+ D130 D130 {CMD[D38]}
            DMOV {CMD[D38]} {MODULE}{CTRLBUF[G1518]}
        
            ;;{CMD[D36]}:={COMMENT}.조그속도(%)
            ;;{CMD[D38]}:={COMMENT}.조그속도(mm)
            ;;{CMD[D44]}:={COMMENT}.인터락.조그속도
            ;;{MODULE}{CTRLBUF[G1518]}:={COMMENT}.조그속도
            
            LDD<= {CMD[D36]} K10
            OUT {CMD[M19]}
            ;;{CMD[M19]}:={COMMENT}.저속.저그허용
        
            LD {CMD[L0]}
            ANI {CMD[D99]}.1
            MPS
            AND {CMD[M20]}
            D+ {CMD[D0]} {CMD[D44]} {CMD[D90]}
            LDI {CMD[M16]}
            OR {CMD[M19]}
            ANB
            OUT {MY2[Y$8]}
            MPP
            AND {CMD[M21]}
            D- {CMD[D0]} {CMD[D44]} {CMD[D90]}
            LDI {CMD[M17]}
            OR {CMD[M19]}
            ANB
            OUT {MY2[Y$9]}
            ;;{CMD[D90]}:={COMMENT}.인터락.비교위치
            ;;{MY2[Y$8]}:={COMMENT}.조그+.출력
            ;;{MY2[Y$9]}:={COMMENT}.조그-.출력
            ;;{CMD[M16]}:={COMMENT}.금지+
            ;;{CMD[M17]}:={COMMENT}.금지-
            
            LD {CMD[M20]}
            OUT {CMD[L20]}
            LD {CMD[M21]}
            OUT {CMD[L21]}
            
            ;;{CMD[M20]}:={COMMENT}.조그+.버튼
            ;;{CMD[M21]}:={COMMENT}.조그-.버튼
            ;;{CMD[L20]}:={COMMENT}.조그+.램프
            ;;{CMD[L21]}:={COMMENT}.조그-.램프
            """)
            
        self.emit_position()
            
        # 위치결정    
        self._emit("""\
            LD= {CMD[D32]} K0
            ANI {CMD[M81]}
            ANI {CMD[L1]}
            OUT {CMD[M86]}
            ;;{CMD[M86]}:={COMMENT}.위치결정.가능
            
            LD {CMD[M86]}
            AND<> {CMD[D31]} K0
            MOV {CMD[D31]} {CMD[D32]}
            LD {CMD[M81]}
            ANI {CMD[M82]}
            OR {CMD[L1]}
            AND<> {CMD[D31]} {CMD[D32]}
            SET {CMD[M9]}
            LD<> {CMD[D32]} K0
            ANI {CMD[L1]}
            ANI {CMD[M9]}
            OUT {CMD[M80]}

            LD {CMD[M80]}
            MOV {CMD[D32]} {CMD[D62]}
            ;;{CMD[D62]}:={COMMENT}.요청.위치번호
            
            LD {CMD[M80]}
            ANI {CMD[M81]}
            OUT {CMD[M89]}
            
            # 이동버튼에 대한 목표위치 얻기
            LD {CMD[M89]}
            AND< {CMD[D62]} K100
            - {CMD[D62]} K1 Z0
            + Z0 Z0
            DMOV {CMD[R40]}Z0 {CMD[D46]}
            DMOV {CMD[D33]} {CMD[D48]}
            LDD<= {CMD[D48]} K0
            DMOV R1006 {CMD[D48]}
            
            # 이동명령에 대한 목표위치 얻기
            LD {CMD[M89]}
            AND= {CMD[D62]} K100
            DMOV {CMD[R8]} {CMD[D48]}
            DMOV {CMD[R6]} {CMD[D46]}
            AND {CMD[L33]}
            D+ {CMD[D0]} {CMD[D46]}
            
            # 인터락
            LD {CMD[M89]}
            DMOV {CMD[D46]} {CMD[D90]}
            
            ;;{CMD[D33]}:={COMMENT}.명령속도
            ;;{CMD[D46]}:={COMMENT}.목표위치
            ;;{CMD[D48]}:={COMMENT}.목표속도

            # 위치결정 데이터 설정
            LD {CMD[M89]}
            AND<= {CMD[D62]} K100
            D* {CMD[D6]} {CMD[D48]} D130
            D/ D130 K1000 D130
            DMOV D130 {CMD[D68]}
            D- {CMD[D46]} {CMD[R12]} {CMD[D66]}
            D+ {CMD[R14]} {CMD[D66]}
            - {CMD[D62]} K1 Z0
            * Z0 K10 Z0
            MOV H100 {MODULE}{DATABUF[G2000]}Z0
            FMOV K0 {MODULE}{DATABUF[G2001]}Z0 K9
            DMOV {CMD[D68]} {MODULE}{DATABUF[G2004]}Z0
            DMOV {CMD[D66]} {MODULE}{DATABUF[G2006]}Z0
            
            ;;{CMD[R12]}:={COMMENT}.설비원점
            ;;{CMD[R14]}:={COMMENT}.서보원점
            ;;{CMD[D66]}:={COMMENT}.실행위치
            ;;{CMD[D68]}:={COMMENT}.실행속도

            # 인터락 확인
            LDD> {CMD[D90]} {CMD[D0]}
            OUT {CMD[M90]}
            LDD< {CMD[D90]} {CMD[D0]}
            OUT {CMD[M91]}
            ;;{CMD[M90]}:={COMMENT}.이동+
            ;;{CMD[M91]}:={COMMENT}.이동-

            LD {CMD[80]}
            OR {CMD[81]}
            LD {CMD[M90]}
            AND {CMD[M16]}
            LD {CMD[M91]}
            AND {CMD[M17]}
            ORB
            ANB
            OUT {CMD[M18]}
            ;;{CMD[M18]}:={COMMENT}.명령금지
            
            LD {CMD[M18]}
            SET {CMD[M9]}
            RST {CMD[M88]}

            # 위치결정 시간초과
            LD {CMD[M81]}
            ANI {CMD[L1]}
            OUT {TIMER[T3]} K15
            AND {TIMER[T3]}
            RST {CMD[M81]}

            # 위치결정 실행
            LD {CMD[M89]}
            AND {CMD[M88]}
            G.PSTRT{AXIS} {U} {CMD[D60]} {CMD[M82]}
            ;;{CMD[D60]}:={COMMENT}.요청.데이터
            SET {CMD[M81]}
            
            LD {CMD[M80]}
            OUT {CMD[M88]}
            ;;{CMD[M88]}:={COMMENT}.위치결정.확정
            
            LD {CMD[M82]}
            MPS
            RST {CMD[M81]}
            MRD
            LD= {CMD[D31]} {CMD[D32]}
            ANB
            MOV K0 {CMD[D31]}
            MRD
            MOV K0 {CMD[D32]}
            MPP
            AND {CMD[M83]}
            OUT M0
            
            ;;{CMD[M9]}:={COMMENT}.정지.요청
            ;;{CMD[M80]}:={COMMENT}.위치결정.요청
            ;;{CMD[M81]}:={COMMENT}.위치결정.동작중
            ;;{CMD[M82]}:={COMMENT}.위치결정.완료
            ;;{CMD[M83]}:={COMMENT}.위치결정.이상
            ;;{CMD[M89]}:={COMMENT}.위치결정.준비
            ;;{CMD[D31]}:={COMMENT}.명령.위치번호
            ;;{CMD[D32]}:={COMMENT}.실행.위치번호
            
            ;;{TIMER[T3]}:={COMMENT}.위치결정.시간초과
            
            ;;{CMD[R6]}:={COMMENT}.이동위치
            ;;{CMD[R8]}:={COMMENT}.이동속도
            """)
            
        # 전원
        self._emit("""\
            LD X1060
            AND X1061
            AND {MCCB}
            OUT {CMD[M11]}
            ;;{MCCB}:={COMMENT}.전원.(MCCB)
            """)
            
        self._emit("""\
            LD {CMD[M29]}
            FF {CMD[L10]}
            
            LD {CMD[L10]}
            AND {CMD[M11]}
            OUT {CMD[M10]}
            OUT {CMD[M0]}
            
            LD {CMD[M10]}
            OUT {POWER_MC}
            
            LD {CMD[M10]}
            LD {CMD[L10]}
            AND SM412
            ORB
            OUT {CMD[L29]}
            
            ;;{CMD[M10]}:={COMMENT}.전원출력
            ;;{CMD[M11]}:={COMMENT}.전원확인
            ;;{CMD[L10]}:={COMMENT}.전원명령
            ;;{CMD[M29]}:={COMMENT}.전원버튼
            ;;{CMD[L29]}:={COMMENT}.전원램프
            """)
            
        # 브레이크
        self._emit("""\
            LD {CMD[M10]}
            AND {CMD[L8]}
            OUT {CMD[M12]}
        #if BRAKE
            OUT {BRAKE}
            ;;{BRAKE}:={COMMENT}.BrakeOff.출력
        #endif
            LDI {CMD[M12]}
            OUT {TIMER[T5]} K5
            AND {TIMER[T5]}
            OUT {CMD[M13]}
            ;;{CMD[M12]}:={COMMENT}.BrakeOff
            ;;{CMD[M13]}:={COMMENT}.BrakeOn
            ;;{TIMER[T5]}:={COMMENT}.BrakeOn.지연 
            """)
            
        # 서보오프
        self._emit("""\
            LDI {CMD[M10]}
            AND {CMD[M13]}
            MOV K1 {MODULE}{CTRLBUF[G1551]}
            INV
            MOV K0 {MODULE}{CTRLBUF[G1551]}
            """)
            
        # 축정지
        self._emit("""\
            LD {TIMER[T6]}
            RST {CMD[M9]}
            LD {CMD[M9]}
            OUT {MY[Y$4]}
            MOV K0 {CMD[D31]}
            MOV K0 {CMD[D32]}
            RST {CMD[M81]}
            ANI {CMD[L1]}
            OUT {TIMER[T6]} K5
            
            ;;{TIMER[T6]}:={COMMENT}.정지확인
            ;;{MY[Y$4]}:={COMMENT}.축정지
            """)
            
        # 램프
        self._emit("""\
            LD {CMD[M26]}
            LD {CMD[M81]}
            AND= {CMD[D62]} K9001
            AND SM412
            ORB
            OUT {CMD[L26]}
            ;;{CMD[L26]}:={COMMENT}.원점램프
            
            LD {CMD[M27]}
            LDI {CMD[M18]}
            LD {CMD[M18]}
            AND SM412
            ORB
            ANB
            LD {CMD[M81]}
            AND<= {CMD[D62]} K100
            AND SM412
            ORB
            OUT {CMD[L27]}
            ;;{CMD[L27]}:={COMMENT}.이동램프
            
            LD {CMD[M28]}
            LD {CMD[M9]}
            AND SM412
            ORB
            OUT {CMD[L28]}
            ;;{CMD[L28]}:={COMMENT}.정지램프
            
            LD {CMD[L33]}
            OUT {CMD[M33]}
            ;;{CMD[L33]}:={COMMENT}.상대이동.선택
            ;;{CMD[M33]}:={COMMENT}.상대이동.램프
            """)
            
    def emit_power_state(self):
        pass
        
    # 서보동기
    def emit_sync(self):
        self._emit("""\
            # 조그 버튼
            LDI {CMD[M34]}
            MPS
            ANDP {CMD[M20]}
            SET {SYNC[M20]}
            MRD
            ANDF {CMD[M20]}
            RST {SYNC[M20]}
            MRD
            ANDP {CMD[M21]}
            SET {SYNC[M21]}
            MPP
            ANDF {CMD[M21]}
            RST {SYNC[M21]}
            
            ;;{CMD[M34]}:={COMMENT}.개별조그
            
            # 원점 버튼
            LD {CMD[M26]}
            OUT {SYNC[M26]}
            
            # 명령 버튼
            LD {CMD[M27]}
            OUT {SYNC[M27]}
            
            # 명령
            LD SM400
            MPS
            AND SM401 
            MOV {CMD[D31]} {SYNC[D31]}
            MPP
            DMOV {CMD[R6]} {SYNC[R6]}
            DMOV {CMD[R8]} {SYNC[R8]}
            
            LD {CMD[L33]}
            OUT {SYNC[L33]}
            
            # 정지
            LD {CMD[M09]}
            AND SM401
            SET {SYNC[M09]}
            
            # 이동 명령
            LD SM400
            MOV K4{CMD[M40]} K4{SYNC[M40]}
            DMOV {CMD[D33]} {SYNC[D33]}
            """)
            
    # 
    def emit_position(self):
        offset_i = {}
        for i in range(len(self.plist)):
            p = self.plist[i]
            
            if p.indexing:
                offset_i[p.no] = i
            
            data = {
                "base": (p.offset - 1) * 2, 
                "gpos": (p.no - 1) * 2, 
                "lpos": 40 + i * 2, 
                "state": 40 + i,
                "button": 40 + i,
                "lamp": 70 + i,
                "relative": p.relative,
                "pname": p.name}
            if p.offset > 0:
                data["ibase"] = 40 + offset_i[p.offset] * 2
                
                
            # 위치값 설정
            src = ""
            if p.no:
                if p.relative:
                    src += """\
            LD SM400
            D+ {R9000[%(gpos)s]} %(relative)s {CMD[R%(lpos)s]}
            """
                elif p.offset > 0:
                    src += """\
            LD SM400
            D+ {CMD[R%(ibase)s]} {R9000[%(gpos)s]} {CMD[R%(lpos)s]}
            """
                else:
                    src += """\
            LD SM400
            DMOV {R9000[%(gpos)s]} {CMD[R%(lpos)s]}
            """
            
            src += """\
            ;;{CMD[R%(lpos)s]}:={COMMENT}.%(pname)s
            """
            self._emit(src % data)

        for i in range(len(self.plist)):
            p = self.plist[i]
            data = {
                "base": (p.offset - 1) * 2, 
                "gpos": (p.no - 1) * 2, 
                "lpos": 40 + i * 2, 
                "state": 40 + i,
                "button": 40 + i,
                "lamp": 70 + i, 
                "pname": p.name}
            
            # 위치값 비교 (위치확인)
            src = ""
            if p.indexing:
                src += """\
            LD SM400
            + {CMD[D40]} {CMD[D40]} Z0
            LD {CMD[L3]}
            ANDD= {CMD[D0]} {CMD[R%(lpos)s]}Z0
            OUT {CMD[L%(state)s]}
            LD {CMD[L%(state)s]}
            LD {CMD[M%(button)s]}
            AND SM412
            ORB
            OUT {CMD[L%(lamp)s]}
            """ % data
            elif p.offset == -1:
                src += """\
            LD {CMD[L3]}
            ANDD= {CMD[D0]} {CMD[R%(lpos)s]}
            OUT {CMD[L%(state)s]}
            LD {CMD[L%(state)s]}
            LD {CMD[M%(button)s]}
            AND SM412
            ORB
            OUT {CMD[L%(lamp)s]}
            """ % data
            
            src += """\
            ;;{CMD[L%(state)s]}:={COMMENT}.%(pname)s
            ;;{CMD[L%(lamp)s]}:={COMMENT}.%(pname)s.램프
            """ % data
            
            self._emit(src)
            
        self._emit("""\
            LD= {CMD[D31]} K0
            MEP
            OR {CMD[M9]}
            OUT {CMD[M87]}
            #;;{CMD[M87]}:={COMMENT}.이동명령.종료
            """)
            
        for i in range(len(self.plist)):
            p = self.plist[i]
            data = {
                "pno": i + 1,
                "pos": 40 + i * 2, 
                "button": 40 + i, 
                "pname": p.name,
                "mask": 1 << i}
                
            if p.indexing:
                self._emit("""\
            LDP {CMD[M%(button)s]}
            MOV H%(mask)X K4{CMD[M40]} 
            LD {CMD[M%(button)s]}
            RST {CMD[L33]}
            + K%(pno)s {CMD[D40]} {CMD[D41]}
            MOV {CMD[D41]} {CMD[D31]}
            LDF {CMD[M%(button)s]}
            AND= {CMD[D31]} {CMD[D41]}
            MOV K0 {CMD[D31]}
            LD {CMD[L%(button)s]}
            OR {CMD[M87]}
            RST {CMD[M%(button)s]}
            
            ;;{CMD[M%(button)s]}:={COMMENT}.%(pname)s.이동명령
            """ % data)
            elif p.offset == -1:
                self._emit("""\
            LDP {CMD[M%(button)s]}
            MOV H%(mask)X K4{CMD[M40]} 
            LD {CMD[M%(button)s]}
            RST {CMD[L33]}
            MOV K%(pno)s {CMD[D31]}
            LDF {CMD[M%(button)s]}
            AND= {CMD[D31]} K%(pno)s
            MOV K0 {CMD[D31]}
            LD {CMD[L%(button)s]}
            OR {CMD[M87]}
            RST {CMD[M%(button)s]}
            
            ;;{CMD[M%(button)s]}:={COMMENT}.%(pname)s.이동명령
            """ % data)

    def emit_teaching(self):
        L = []
        for p in self.plist:
            if p.no and p.offset == -1 and p.no not in L:
                self._emit("""\
            LD {M9000[%(pno)s]}
            RST {CMD[L33]}
            DMOV {R9000[%(pvalue)s]} {CMD[R6]}
            AND T1041
            DMOV {CMD[D0]} {R9000[%(pvalue)s]}
            LDD= {R9000[%(pvalue)s]} {CMD[D0]}
            LDD= {R9000[%(pvalue)s]} {CMD[R6]}
            ANI {CMD[L33]}
            AND SM412
            ORB
            OUT {L9000[%(pno)s]}
            
            ;;{M9000[%(pno)s]}:=위치표.위치%(pp)s.선택버튼
            ;;{L9000[%(pno)s]}:=위치표.위치%(pp)s.선택램프
            ;;{R9000[%(pvalue)s]}:=위치표.위치%(pp)s
            """ % {"pno": p.no - 1, "pvalue": (p.no - 1) * 2, "pp": p.no})
            elif p.no > 0:
                self._emit("""\
            ;;{R9000[%(pvalue)s]}:=위치표.위치%(pp)s.(OFFSET)
            """ % {"pno": p.no - 1, "pvalue": (p.no - 1) * 2, "pp": p.no})
            L.append(p.no)
            
    def emit(self):
        self.emit_buffer()
        if self.sync:
            self.emit_sync()
      
      
# 여러기구를 하나처럼 움직이는 경우를 제어한다.
# 일부의 장치를 사용하지 않는 것으로 설정할 수 있다.
class MultiUnit(FaUnit):
    def __init__(self, *args, **kwargs):
        super(MultiUnit, self).__init__(*args, **kwargs)
        
    def set_data(self, name, value):
        setattr(self, name, value)
        return self
        
    def emit_state(self):
        count = max(len(self.state1), len(self.state2))
        L = []
        for i in range(count):
            if self.state1:
                L.append("LD " + self.state1[i])
                L.append("OUT {{CMD[D0}}.{0:X}".format(i))
            elif self.state2:
                L.append("LDI " + self.state2[i])
                if self.output1:
                    L.append("AND " + self.output1[i])
                L.append("OUT {{CMD[D0}}.{0:X}".format(i))
            if self.state2: 
                L.append("LD " + self.state2[i])
                L.append("OUT {{CMD[D1}}.{0:X}".format(i))
            elif self.state1:
                L.append("LDI " + self.state1[i])
                if self.output2:
                    L.append("AND " + self.output2[i])
                L.append("OUT {{CMD[D1}}.{0:X}".format(i))
        self._emit("\n".join(L))
            
    def emit_output(self):
        count = max(len(self.state1), len(self.state2))
        L = []
        for i in range(count):
            if self.output1:
                L.append("LD {{CMD[D2}}.{0:X}".format(i))
                L.append("OUT " + self.output1[i])
            if self.output2:
                L.append("LD {{CMD[D3}}.{0:X}".format(i))
                L.append("OUT " + self.output2[i])
        self._emit("\n".join(L))
        
    def emit(self):
        self.emit_state()
        self.emit_output()
        
            
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    
unittypetable = {
    "stopper": Stopper,
    "diverter": BackupCylinder,
    "backup": BackupCylinder,
    "sidest" : Cylinder,
    "turn" : TurnCylinder,
    "inverter": Inverter,
    "conveyer": Conveyer,
    "pm": PowerMoller,
    "lifter": Shuttle,
    "servo": Servo,
    "pinup": BackupCylinder,
    "clamp": ClampCylinder,
    "forback": Cylinder,
    "boxconv": BoxConveyer,
}

def get_unittype(unitname):
    if unitname in unittypetable:
        return unittypetable[unitname]
    raise KeyError(unitname)
    
    
class AliasDictItem(object):
    def __init__(self, owner):
        self.owner = owner
        
    def set_alias(self, *args):
        self.alias = args[0] if args else self.name
        for alias in args:
            self.owner.set_alias(name, alias)
        return self
    
    
class AliasDict(OrderedDict):
    def __init__(self):
        super(AliasDict, self).__init__()
        self.aliases = {}
        
    def set_alias(self, name, alias):
        self.aliases[alias] = name
        
    def __getitem__(self, name):
        if name not in self:
            if name in self.aliases:
                name = self.self.aliases[name]
            else:
                raise KeyError(name)
        return super(AliasDict, self).__getitem__(name)
        
        
class Part(FaObject):
    def __init__(self, line, id, name, no=0, parent=None):
        super(Part, self).__init__(line, id, name)
        self.no = no
        self.parent = parent
        self.opboxs = OrderedDict()
        self.zones = []
        self.subparts = []
        self.vars["CMD"] = self.id
        self.vars["COMMENT"] = self.name
        self.emergency = None
        self.manuallock = None
        self.reset = "M1003"
        self.longreset = "M1004"
        self.initialize = None
        self.pause = None

    def add_opbox(self, id, name):
        self.opboxs[name] = opbox = OpBox(self, id, name)
        return opbox
        
    def set_subparts(self, *args):
        for arg in args:
            self.subparts.append(arg)

    def set_zone(self, *args):
        for arg in args:
            self.zones.append(arg)
            
    def emit_comment(self):
        self._emit("""\
            ;;{CMD[L0]}:={COMMENT}.초기화
            ;;{CMD[L1]}:={COMMENT}.비상정지
            ;;{CMD[L2]}:={COMMENT}.수동금지
            ;;{CMD[L3]}:={COMMENT}.리셋
            ;;{CMD[L4]}:={COMMENT}.일시정지
            """)
            
    def emit_manual_lock(self):
        # 같은 이름의 OP를 찾는다.
        op = self.line.get_opbox(self.name)
        if op:
            self.vars["MANUAL"] = op.get_manual()
            self._emit("""\
            LDI {MANUAL}
            OUT {CMD[L2]}
            """)
        else:
            self._emit("""\
            LD {CMD[M1]}
            OUT {CMD[L2]}
            """)
            
    def emit_link_subparts(self):
        for name in self.subparts:
            subpart = self.line.get_part(name)
        
    def emit(self):
        self.emit_comment()
        self.emit_manual_lock()
        self.emit_link_subparts()

        
class FaLine(object):
    def __init__(self):
        self.parts = OrderedDict()
        self.zones = OrderedDict()
        self.units = OrderedDict()
        self.opboxs = AliasDict()
        self.steps = OrderedDict()
        self.names = {}
        
    def add_part(self, id, name, no, **kwargs):
        part = self.parts[name] = Part(self, id, name, no, **kwargs)
        return part
        
    def add_opbox(self, id, name):
        opbox = self.opboxs[name] = OpBox(self, id, name)
        return opbox
        
    def add_zone(self, cmd, name):
        zone = self.zones[name] = Zone(self, cmd, name)
        return zone
        
    def add_unit(self, *args, **kwargs):
        unitkind = args[3]
        unitclass = get_unittype(unitkind)
        args = args[:3] + args[4:]
        unit = unitclass(self, *args, **kwargs)
        self.units["{0}.{1}".format(args[1], args[2])] = unit
        unit.factory = unitclass
        unit.kind = unitkind
        return unit
        
    def add_unit2(self, *args, **kwargs):
        unitclass = get_unittype(args[3])
        unit = unitclass(self, *args, **kwargs)
        self.units[unit.keyname] = unit
        unit.factory = unitclass
        unit.kind = args[3]
        return unit
        
    def add_step(self, id, from_, to):
        step = self.steps[id] = OutputStep(self, id, from_, to)
        return step
        
    def load(self, workbook):
        self.zones = OrderedDict()
        sheet = workbook["구간"]
        for i in range(sheet.lastrowno() + 1):
            row = sheet[i]
            if row[1]:
                zone = self.zones[row[1]] = Zone(self, row[0], row[1])
                zone.set_zone_sensor(row[2], row[3])
                
        self.names = {}
        sheet = workbook["구간명"]
        for i in range(sheet.lastrowno() + 1):
            row = sheet[i]
            self.names[row[0]] = row[1]
            
        self.load_units(workbook)
        
        return self
        
    def load_units(self, workbook):
        self.units = OrderedDict()
        sheet = workbook[u"유닛"]
        for i in range(1, sheet.lastrowno() + 1):
            row = sheet[i]
            if row[1]:
                unitclass = get_unittype(row[3])
                unit = self.units["{0}.{1}".format(row[1], row[2])] = unitclass.create(self, row)
            
    def __getitem__(self, name):
        return self.getzone(name)
        
    def get_unit(self, *names):
        for name in names:
            value = self.units.get(name)
            if value:
                return value
        return None
        
    def get_part(self, name):
        part = self.parts.get(name)
        if part:
            return part
        for part in self.parts.values():
            if name in part.zones:
                return part
        return None
        
    def get_opbox(self, name):
        return self.opboxs.get(name)
    
    def get_zone(self, name):
        if name not in self.zones:
            if name in self.names:
                name = self.names[name]
            else:
                raise KeyError(name)
        return self.zones[name]
    getzone = get_zone
    
    def _emit(self, code, vars=None):
        if not vars:
            vars = self.vars
        macro = LadderMacro(code, vars=vars)
        #print macro.render()
        emiter.emit(macro.render())
    
    def emit_servo_power_state(self):
        pass
    
    def __iter__(self):
        return self.zones.itervalues()
        
    def addUnit(self, unit):
        pass
        
    def emit_emgcstop(self, hold, display, *buttons):
        if buttons:
            vars = { "HOLD": hold, "DISPLAY": display, "BUTTON": "M1" }
            src = ""
            for i in range(len(buttons)):
                button = buttons[i]
                src += """\
                    LD %s
                    OUT {DISPLAY}.%X
                    """ % (button, i)
            src += """
            LD<> {DISPLAY} K0
            OUT M1
            LD {HOLD[M0]}
            ANI M1004
            OR M1
            OUT {HOLD[M0]}
            LDI M1
            OR SM411
            ANB
            OUT {HOLD[L0]}
            """
            self._emit(src, vars)
     
    def emit_dooropen(self, hold, door, display):
        src = ""
        vars = { "HOLD": hold, "DISPLAY": display, "DOOR": door }
        src += """
            LD {HOLD[M0]}
            ANI M1004
            ORI {DOOR}
            OUT {HOLD[M0]}
            LD {DOOR}
            OR SM411
            ANB
            OUT {HOLD[L0]}
            OUT D1002.{DISPLAY}
            """
        self._emit(src, vars)
        
        
    def alarm(self):
        _alarm.report(self, "alarm.csv")

class UnitTable(object):
    def __init__(self, workbook):
        self.units = OrderedDict()
        sheet = workbook[u"유닛"]
        for i in range(1, sheet.lastrowno() + 1):
            row = sheet[i]
            if row[1]:
                unitclass = get_unittype(row[3])
                unit = self.units["{0}.{1}".format(row[1], row[2])] = unitclass.create(row)
                
    def __getitem__(self, key):
        if type(key) == types.IntType:
            key = self.units.keys()[key]
        return self.units[key]
        
    def __iter__(self):
        return self.units.itervalues()
        
    def write(self, sw):
        sw.write('units = [')
        sw.indent()
        for unit in self.units.values():
            unit.dump(sw)
        sw.write(']')
        sw.unindent()        
        
def make_output_step_name(fr, to):
    fr = fr.split(".")
    to = to.split(".")
    
    if len(fr) == len(to) == 1:
        pass
        
        
        
class OutputStep(object):
    def __init__(self, line, id, from_, to):
        self.vars = {}
        self.line = line
        self.id = id
        self.from_ = from_
        self.to = to
        
        self.vars["ID"] = self.id
        self.vars["STEP"] = self.id
        self.vars["STEPRUN"] = "M" + self.id[1:]
        self.vars["STEPSTOP"] = "L" + self.id[1:]
        self.vars["STEPTIMER"] = "T" + self.id[1:]
        self.vars["STEPNO"] = "D" + self.id[1:]
        self.vars["STEPFUNC"] = "P" + self.id[1:]
        self.vars["COMMENT"] = from_
        self.vars["ALARM"] = "M8" + self.id[1:]
        
        self.vars["FROM"] = self.line.get_zone(from_).id
        self.vars["TO"] = self.line.get_zone(to).id
        
        
        st = self.line.get_unit(from_ + ".S/T", from_ + ".종S/T")
        if st:
            self.vars["ST_CMD"] = st.id
        pm = self.line.get_unit(from_ + ".P/M", from_ + ".종P/M")
        if pm:
            self.vars["FR_PM"] = pm.id
        pm = self.line.get_unit(to + ".P/M", to + ".종P/M")
        if pm:
            self.vars["TO_PM"] = pm.id
        dv1 = self.line.get_unit(from_ + ".D/V")
        if dv1:
            self.vars["FR_DV"] = dv1.id
        dv2 = self.line.get_unit(to + ".D/V")
        if dv2:
            self.vars["TO_DV"] = dv2.id
        self.dv = dv1 or dv2
        
    def _emit(self, code):
        macro = LadderMacro(code, vars=self.vars)
        src = macro.render()
        print self._trans(src)
        
    def _trans(self, src):
        return src
        
    def emit_auto(self):
        self._emit("""
            LD SM401
            AND {FROM[T0]}
            AND {TO[T1]}
            SET {STEPRUN}
            """)
        
    def emit_step_call(self):
        self._emit("""\
            LD {STEPRUN}
            ORF {STEPRUN} 
            CALL {STEPFUNC}
            """)
            
    def emit_begin_step(self):
        self._emit("""\
            ;;{STEPRUN}:={COMMENT}.배출스텝.실행
            ;;{STEPSTOP}:={COMMENT}.배출스텝.정지요청
            ;;{STEPNO}:={COMMENT}.배출스텝.스텝번호
            ;;{STEPTIMER}:={COMMENT}.배출스텝.시간초과
            ;;{STEPFUNC}:={COMMENT}.배출스텝
            {STEPFUNC}
            LD SM400
            MOV {STEPNO} D180
            LD<> {STEPNO} K0
            OUT {STEPTIMER} R1023
            LDI {STEPRUN}
            LD {STEPTIMER}
            AND {STEPSTOP}
            ORB
            MOV K60 D180
            LD SM400
            DECO D180 M100 K6
            LD M100
            AND {STEPRUN}
            MOV K1 D180
            """)
        
    def emit_end_step(self):
        self._emit("""\
            LD<> D180 {STEPNO}
            RST {STEPTIMER}
            MOV D180 {STEPNO}
            RET
            """)
            
    def emit_dv_updn(self):
        if self.dv:
            self._emit("""\
            LD M101
        #if FR_DV
            SET {FR_DV[M0]}
        #endif
        #if TO_DV
            SET {TO_DV[M0]}
        #endif
        #if FR_DV
            AND {FR_DV[L0]}
        #endif
        #if TO_DV
            AND {TO_DV[L0]}
        #endif
            MOV K3 D180
            """)
        else:
            self._emit("""\
            LD M101
            MOV K3 D180
            """)
            
    def emit_st_down(self):
        self._emit("""\
            LD M103
        #if ST_CMD
            SET {ST_CMD[M1]}
            AND {ST_CMD[L1]}
        #endif
            MOV K4 D180
            """)
            
    def emit_output(self):
        self._emit("""\
            LD M104
            MPS
            OUT M0
        #if FR_PM
            SET {FR_PM[M0]}
        #endif
        #if TO_PM
            SET {TO_PM[M0]}
        #endif
            AND {STEPTIMER}
            MOV K40 D180
            MPP 
            AND {TO[T0]}
            MOV K8 D180
            """)
            
    def emit_arrive(self):
        self._emit("""\
            LD M108
            MOV K60 D180
            """)
            
    def emit_error(self):
        self._emit("""\
            LD M140
            OUT {ALARM}
            LD M1003
            LD {STEPSTOP}
            ORB
            LD {FROM[T1]}
            AND {TO[T1]}
            ORB
            ANB
            MOV K60 D180
            """)
            
    def emit_clear0(self):
        self._emit("""\
            LD M160
            RST {STEPRUN}
            RST {STEPSTOP}
            MOV K0 D180
            SET M161
            """)
            
    def emit_clear1(self):
        self._emit("""\
            LD M161
            OUT M0
        #if FR_PM
            RST {FR_PM[M0]}
        #endif
        #if TO_PM
            RST {TO_PM[M0]}
        #endif
            """)
            
    def emit_normal(self):
        self.emit_begin_step()
        self.emit_dv_updn()
        self.emit_st_down()
        self.emit_output()
        self.emit_arrive()
        self.emit_error()
        self.emit_clear0()
        self.emit_clear1()
        self.emit_end_step()
        
    def emit_turn(self):
        updn = self.line.get_unit(self.from_ + ".TURNUP")
        turn = self.line.get_unit(self.from_ + ".TURN")
        
        
    def emit_lastout(self):
        self.emit_begin_step()
        self.emit_dv_updn()
        self.emit_st_down()
        self.emit_output()
        self.emit_arrive()
        self.emit_error()
        self.emit_clear0()
        self.emit_clear1()
        self.emit_end_step()
        
    def emit(self):
        if self.from_ == "검사3&4.배출":
            self.emit_lastout()
        else:
            self.emit_normal()
            
    def alarm(self):
        part = self.line.get_part(self.from_)
        zone_no = 0
        if part:
            zone_no = part.no
        else:
            part = self.line.get_part(self.to)
            if part:
                zone_no = part.no
            else:
                import sys
                print >>sys.stderr, "({0} -> {1})의 파트를 찾을 수 없습니다.".format(self.from_, self.to)
        return "{2},5,0,0,2,스텝 시간초과 - {0} -> {1}".format(self.from_, self.to, zone_no)
        
        
        
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        
if __name__ == '__main__':
    pass

             